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"
44 #include "canvas/canvas.h"
45 #include "canvas/scroll_group.h"
50 #include "audio_region_view.h"
51 #include "automation_region_view.h"
52 #include "midi_region_view.h"
53 #include "ardour_ui.h"
54 #include "gui_thread.h"
55 #include "control_point.h"
56 #include "region_gain_line.h"
57 #include "editor_drag.h"
58 #include "audio_time_axis.h"
59 #include "midi_time_axis.h"
60 #include "selection.h"
61 #include "midi_selection.h"
62 #include "automation_time_axis.h"
64 #include "editor_cursors.h"
65 #include "mouse_cursors.h"
66 #include "note_base.h"
67 #include "patch_change.h"
68 #include "ui_config.h"
69 #include "verbose_cursor.h"
72 using namespace ARDOUR;
75 using namespace Gtkmm2ext;
76 using namespace Editing;
77 using namespace ArdourCanvas;
79 using Gtkmm2ext::Keyboard;
81 double ControlPointDrag::_zero_gain_fraction = -1.0;
83 DragManager::DragManager (Editor* e)
86 , _current_pointer_x (0.0)
87 , _current_pointer_y (0.0)
88 , _current_pointer_frame (0)
89 , _old_follow_playhead (false)
93 DragManager::~DragManager ()
98 /** Call abort for each active drag */
100 DragManager::abort ()
104 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
109 if (!_drags.empty ()) {
110 _editor->set_follow_playhead (_old_follow_playhead, false);
114 _editor->abort_reversible_command();
120 DragManager::add (Drag* d)
122 d->set_manager (this);
123 _drags.push_back (d);
127 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
129 d->set_manager (this);
130 _drags.push_back (d);
135 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
137 /* Prevent follow playhead during the drag to be nice to the user */
138 _old_follow_playhead = _editor->follow_playhead ();
139 _editor->set_follow_playhead (false);
141 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
143 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 (*i)->start_grab (e, c);
148 /** Call end_grab for each active drag.
149 * @return true if any drag reported movement having occurred.
152 DragManager::end_grab (GdkEvent* e)
157 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
158 bool const t = (*i)->end_grab (e);
169 _editor->set_follow_playhead (_old_follow_playhead, false);
175 DragManager::mark_double_click ()
177 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
178 (*i)->set_double_click (true);
183 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
187 /* calling this implies that we expect the event to have canvas
190 * Can we guarantee that this is true?
193 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
195 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
196 bool const t = (*i)->motion_handler (e, from_autoscroll);
197 /* run all handlers; return true if at least one of them
198 returns true (indicating that the event has been handled).
210 DragManager::have_item (ArdourCanvas::Item* i) const
212 list<Drag*>::const_iterator j = _drags.begin ();
213 while (j != _drags.end() && (*j)->item () != i) {
217 return j != _drags.end ();
220 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
224 , _pointer_frame_offset (0)
225 , _x_constrained (false)
226 , _y_constrained (false)
227 , _was_rolling (false)
228 , _trackview_only (trackview_only)
229 , _move_threshold_passed (false)
230 , _starting_point_passed (false)
231 , _initially_vertical (false)
232 , _was_double_click (false)
235 , _last_pointer_x (0.0)
236 , _last_pointer_y (0.0)
237 , _raw_grab_frame (0)
239 , _last_pointer_frame (0)
241 , _snap_delta_music (0.0)
242 , _constraint_pressed (false)
248 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
254 _cursor_ctx = CursorContext::create (*_editor, cursor);
256 _cursor_ctx->change (cursor);
263 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
266 /* we set up x/y dragging constraints on first move */
267 _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
269 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
271 setup_pointer_frame_offset ();
272 _grab_frame = adjusted_frame (_raw_grab_frame, event).frame;
273 _last_pointer_frame = _grab_frame;
274 _last_pointer_x = _grab_x;
276 if (_trackview_only) {
277 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
280 _last_pointer_y = _grab_y;
284 if (!_editor->cursors()->is_invalid (cursor)) {
285 /* CAIROCANVAS need a variant here that passes *cursor */
286 _cursor_ctx = CursorContext::create (*_editor, cursor);
289 if (_editor->session() && _editor->session()->transport_rolling()) {
292 _was_rolling = false;
295 switch (_editor->snap_type()) {
296 case SnapToRegionStart:
297 case SnapToRegionEnd:
298 case SnapToRegionSync:
299 case SnapToRegionBoundary:
300 _editor->build_region_boundary_cache ();
307 /** Call to end a drag `successfully'. Ungrabs item and calls
308 * subclass' finished() method.
310 * @param event GDK event, or 0.
311 * @return true if some movement occurred, otherwise false.
314 Drag::end_grab (GdkEvent* event)
316 _editor->stop_canvas_autoscroll ();
320 finished (event, _move_threshold_passed);
322 _editor->verbose_cursor()->hide ();
325 return _move_threshold_passed;
329 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
331 MusicFrame pos (0, 0);
333 if (f > _pointer_frame_offset) {
334 pos.frame = f - _pointer_frame_offset;
338 _editor->snap_to_with_modifier (pos, event);
345 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
347 return adjusted_frame (_drags->current_pointer_frame (), event, snap).frame;
351 Drag::snap_delta (guint state) const
353 if (ArdourKeyboard::indicates_snap_delta (state)) {
360 Drag::snap_delta_music (guint state) const
362 if (ArdourKeyboard::indicates_snap_delta (state)) {
363 return _snap_delta_music;
370 Drag::current_pointer_x() const
372 return _drags->current_pointer_x ();
376 Drag::current_pointer_y () const
378 if (!_trackview_only) {
379 return _drags->current_pointer_y ();
382 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
386 Drag::setup_snap_delta (MusicFrame pos)
388 TempoMap& map (_editor->session()->tempo_map());
389 MusicFrame snap (pos);
390 _editor->snap_to (snap, ARDOUR::RoundNearest, false, true);
391 _snap_delta = snap.frame - pos.frame;
393 _snap_delta_music = 0.0;
395 if (_snap_delta != 0) {
396 _snap_delta_music = map.exact_qn_at_frame (snap.frame, snap.division) - map.exact_qn_at_frame (pos.frame, pos.division);
401 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
403 /* check to see if we have moved in any way that matters since the last motion event */
404 if (_move_threshold_passed &&
405 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
406 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
410 pair<framecnt_t, int> const threshold = move_threshold ();
412 bool const old_move_threshold_passed = _move_threshold_passed;
414 if (!_move_threshold_passed) {
416 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
417 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
419 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
422 if (active (_editor->mouse_mode) && _move_threshold_passed) {
424 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
426 if (old_move_threshold_passed != _move_threshold_passed) {
430 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
431 _initially_vertical = true;
433 _initially_vertical = false;
435 /** check constraints for this drag.
436 * Note that the current convention is to use "contains" for
437 * key modifiers during motion and "equals" when initiating a drag.
438 * In this case we haven't moved yet, so "equals" applies here.
440 if (Config->get_edit_mode() != Lock) {
441 if (event->motion.state & Gdk::BUTTON2_MASK) {
442 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
443 if (_constraint_pressed) {
444 _x_constrained = false;
445 _y_constrained = true;
447 _x_constrained = true;
448 _y_constrained = false;
450 } else if (_constraint_pressed) {
451 // if dragging normally, the motion is constrained to the first direction of movement.
452 if (_initially_vertical) {
453 _x_constrained = true;
454 _y_constrained = false;
456 _x_constrained = false;
457 _y_constrained = true;
461 if (event->button.state & Gdk::BUTTON2_MASK) {
462 _x_constrained = false;
464 _x_constrained = true;
466 _y_constrained = false;
470 if (!from_autoscroll) {
471 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
474 if (!_editor->autoscroll_active() || from_autoscroll) {
477 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
479 motion (event, first_move && !_starting_point_passed);
481 if (first_move && !_starting_point_passed) {
482 _starting_point_passed = true;
485 _last_pointer_x = _drags->current_pointer_x ();
486 _last_pointer_y = current_pointer_y ();
487 _last_pointer_frame = adjusted_current_frame (event, false);
497 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
505 aborted (_move_threshold_passed);
507 _editor->stop_canvas_autoscroll ();
508 _editor->verbose_cursor()->hide ();
512 Drag::show_verbose_cursor_time (framepos_t frame)
514 _editor->verbose_cursor()->set_time (frame);
515 _editor->verbose_cursor()->show ();
519 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
521 _editor->verbose_cursor()->set_duration (start, end);
522 _editor->verbose_cursor()->show ();
526 Drag::show_verbose_cursor_text (string const & text)
528 _editor->verbose_cursor()->set (text);
529 _editor->verbose_cursor()->show ();
532 boost::shared_ptr<Region>
533 Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
535 if (_editor->session()) {
536 const TempoMap& map (_editor->session()->tempo_map());
537 framecnt_t pos = grab_frame();
538 /* not that the frame rate used here can be affected by pull up/down which
541 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
542 return view->add_region (grab_frame(), len, commit);
545 return boost::shared_ptr<Region>();
548 struct PresentationInfoTimeAxisViewSorter {
549 bool operator() (TimeAxisView* a, TimeAxisView* b) {
550 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
554 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
559 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
561 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
562 as some of the regions we are dragging may be on such tracks.
565 TrackViewList track_views = _editor->track_views;
566 track_views.sort (PresentationInfoTimeAxisViewSorter ());
568 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
569 _time_axis_views.push_back (*i);
571 TimeAxisView::Children children_list = (*i)->get_child_list ();
572 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
573 _time_axis_views.push_back (j->get());
577 /* the list of views can be empty at this point if this is a region list-insert drag
580 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
581 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
584 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
588 RegionDrag::region_going_away (RegionView* v)
590 list<DraggingView>::iterator i = _views.begin ();
591 while (i != _views.end() && i->view != v) {
595 if (i != _views.end()) {
600 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
601 * or -1 if it is not found.
604 RegionDrag::find_time_axis_view (TimeAxisView* t) const
607 int const N = _time_axis_views.size ();
608 while (i < N && _time_axis_views[i] != t) {
612 if (_time_axis_views[i] != t) {
619 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
620 : RegionDrag (e, i, p, v)
622 , _ignore_video_lock (false)
623 , _last_position (0, 0)
625 , _last_pointer_time_axis_view (0)
626 , _last_pointer_layer (0)
631 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
635 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
637 Drag::start_grab (event, cursor);
638 setup_snap_delta (_last_position);
640 show_verbose_cursor_time (_last_position.frame);
642 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
644 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
645 assert(_last_pointer_time_axis_view >= 0);
646 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
649 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
650 _ignore_video_lock = true;
654 /* cross track dragging seems broken here. disabled for now. */
655 _y_constrained = true;
660 RegionMotionDrag::compute_x_delta (GdkEvent const * event, MusicFrame* pending_region_position)
662 /* compute the amount of pointer motion in frames, and where
663 the region would be if we moved it by that much.
665 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
667 framecnt_t sync_offset;
670 sync_offset = _primary->region()->sync_offset (sync_dir);
672 /* we don't handle a sync point that lies before zero.
674 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position->frame >= sync_offset)) {
676 framecnt_t const sd = snap_delta (event->button.state);
677 MusicFrame sync_snap (pending_region_position->frame + (sync_dir * sync_offset) + sd, 0);
678 _editor->snap_to_with_modifier (sync_snap, event);
679 if (sync_offset == 0 && sd == 0) {
680 *pending_region_position = sync_snap;
682 pending_region_position->set (_primary->region()->adjust_to_sync (sync_snap.frame) - sd, 0);
685 *pending_region_position = _last_position;
688 if (pending_region_position->frame > max_framepos - _primary->region()->length()) {
689 *pending_region_position = _last_position;
694 bool const x_move_allowed = !_x_constrained;
696 if ((pending_region_position->frame != _last_position.frame) && x_move_allowed) {
698 /* x movement since last time (in pixels) */
699 dx = _editor->sample_to_pixel_unrounded (pending_region_position->frame - _last_position.frame);
701 /* total x movement */
702 framecnt_t total_dx = _editor->pixel_to_sample (_total_x_delta + dx);
704 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
705 frameoffset_t const off = i->view->region()->position() + total_dx;
707 dx = dx - _editor->sample_to_pixel_unrounded (off);
708 *pending_region_position = MusicFrame (pending_region_position->frame - off, 0);
718 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
724 const int tavsize = _time_axis_views.size();
725 const int dt = delta > 0 ? +1 : -1;
727 int target = start + delta - skip;
729 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
730 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
732 while (current >= 0 && current != target) {
734 if (current < 0 && dt < 0) {
737 if (current >= tavsize && dt > 0) {
740 if (current < 0 || current >= tavsize) {
744 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
745 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
749 if (distance_only && current == start + delta) {
757 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
759 if (_y_constrained) {
763 const int tavsize = _time_axis_views.size();
764 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
765 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
766 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
768 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
769 /* already in the drop zone */
770 if (delta_track >= 0) {
771 /* downward motion - OK if others are still not in the dropzone */
780 } else if (n >= tavsize) {
781 /* downward motion into drop zone. That's fine. */
785 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
786 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
787 /* not a track, or the wrong type */
791 double const l = i->layer + delta_layer;
793 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
794 mode to allow the user to place a region below another on layer 0.
796 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
797 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
798 If it has, the layers will be munged later anyway, so it's ok.
804 /* all regions being dragged are ok with this change */
808 struct DraggingViewSorter {
809 bool operator() (const DraggingView& a, const DraggingView& b) {
810 return a.time_axis_view < b.time_axis_view;
815 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
817 double delta_layer = 0;
818 int delta_time_axis_view = 0;
819 int current_pointer_time_axis_view = -1;
821 assert (!_views.empty ());
823 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
825 /* Find the TimeAxisView that the pointer is now over */
826 const double cur_y = current_pointer_y ();
827 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
828 TimeAxisView* tv = r.first;
830 if (!tv && cur_y < 0) {
831 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
835 /* find drop-zone y-position */
836 Coord last_track_bottom_edge;
837 last_track_bottom_edge = 0;
838 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
839 if (!(*t)->hidden()) {
840 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
845 if (tv && tv->view()) {
846 /* the mouse is over a track */
847 double layer = r.second;
849 if (first_move && tv->view()->layer_display() == Stacked) {
850 tv->view()->set_layer_display (Expanded);
853 /* Here's the current pointer position in terms of time axis view and layer */
854 current_pointer_time_axis_view = find_time_axis_view (tv);
855 assert(current_pointer_time_axis_view >= 0);
857 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
859 /* Work out the change in y */
861 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
862 if (!rtv || !rtv->is_track()) {
863 /* ignore non-tracks early on. we can't move any regions on them */
864 } else if (_last_pointer_time_axis_view < 0) {
865 /* Was in the drop-zone, now over a track.
866 * Hence it must be an upward move (from the bottom)
868 * track_index is still -1, so delta must be set to
869 * move up the correct number of tracks from the bottom.
871 * This is necessary because steps may be skipped if
872 * the bottom-most track is not a valid target and/or
873 * if there are hidden tracks at the bottom.
874 * Hence the initial offset (_ddropzone) as well as the
875 * last valid pointer position (_pdropzone) need to be
876 * taken into account.
878 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
880 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
883 /* TODO needs adjustment per DraggingView,
885 * e.g. select one region on the top-layer of a track
886 * and one region which is at the bottom-layer of another track
889 * Indicated drop-zones and layering is wrong.
890 * and may infer additional layers on the target-track
891 * (depending how many layers the original track had).
893 * Or select two regions (different layers) on a same track,
894 * move across a non-layer track.. -> layering info is lost.
895 * on drop either of the regions may be on top.
897 * Proposed solution: screw it :) well,
898 * don't use delta_layer, use an absolute value
899 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
900 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
901 * 3) iterate over all DraggingView, find the one that is over the track with most layers
902 * 4) proportionally scale layer to layers available on target
904 delta_layer = current_pointer_layer - _last_pointer_layer;
907 /* for automation lanes, there is a TimeAxisView but no ->view()
908 * if (!tv) -> dropzone
910 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
911 /* Moving into the drop-zone.. */
912 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
913 /* delta_time_axis_view may not be sufficient to move into the DZ
914 * the mouse may enter it, but it may not be a valid move due to
917 * -> remember the delta needed to move into the dropzone
919 _ddropzone = delta_time_axis_view;
920 /* ..but subtract hidden tracks (or routes) at the bottom.
921 * we silently move mover them
923 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
924 - _time_axis_views.size();
926 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
927 /* move around inside the zone.
928 * This allows to move further down until all regions are in the zone.
930 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
931 assert(ptr_y >= last_track_bottom_edge);
932 assert(_ddropzone > 0);
934 /* calculate mouse position in 'tracks' below last track. */
935 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
936 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
938 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
940 delta_time_axis_view = dzpos - _pdropzone;
941 } else if (dzpos < _pdropzone && _ndropzone > 0) {
942 // move up inside the DZ
943 delta_time_axis_view = dzpos - _pdropzone;
947 /* Work out the change in x */
948 TempoMap& tmap = _editor->session()->tempo_map();
949 MusicFrame pending_region_position (0, 0);
950 double const x_delta = compute_x_delta (event, &pending_region_position);
952 double const last_pos_qn = tmap.exact_qn_at_frame (_last_position.frame, _last_position.division);
953 double const qn_delta = tmap.exact_qn_at_frame (pending_region_position.frame, pending_region_position.division) - last_pos_qn;
955 _last_position = pending_region_position;
957 /* calculate hidden tracks in current y-axis delta */
959 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
960 /* The mouse is more than one track below the dropzone.
961 * distance calculation is not needed (and would not work, either
962 * because the dropzone is "packed").
964 * Except when [partially] moving regions out of dropzone in a large step.
965 * (the mouse may or may not remain in the DZ)
966 * Hidden tracks at the bottom of the TAV need to be skipped.
968 * This also handles the case if the mouse entered the DZ
969 * in a large step (exessive delta), either due to fast-movement,
970 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
972 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
973 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
975 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
976 -_time_axis_views.size() - dt;
979 else if (_last_pointer_time_axis_view < 0) {
980 /* Moving out of the zone. Check for hidden tracks at the bottom. */
981 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
982 -_time_axis_views.size() - delta_time_axis_view;
984 /* calculate hidden tracks that are skipped by the pointer movement */
985 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
986 - _last_pointer_time_axis_view
987 - delta_time_axis_view;
990 /* Verify change in y */
991 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
992 /* this y movement is not allowed, so do no y movement this time */
993 delta_time_axis_view = 0;
998 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
999 /* haven't reached next snap point, and we're not switching
1000 trackviews nor layers. nothing to do.
1005 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
1006 PlaylistDropzoneMap playlist_dropzone_map;
1007 _ndropzone = 0; // number of elements currently in the dropzone
1010 /* sort views by time_axis.
1011 * This retains track order in the dropzone, regardless
1012 * of actual selection order
1014 _views.sort (DraggingViewSorter());
1016 /* count number of distinct tracks of all regions
1017 * being dragged, used for dropzone.
1019 int prev_track = -1;
1020 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1021 if (i->time_axis_view != prev_track) {
1022 prev_track = i->time_axis_view;
1028 _views.back().time_axis_view -
1029 _views.front().time_axis_view;
1031 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1032 - _views.back().time_axis_view;
1034 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1038 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1040 RegionView* rv = i->view;
1045 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1052 /* reparent the regionview into a group above all
1056 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1057 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1058 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1059 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1060 /* move the item so that it continues to appear at the
1061 same location now that its parent has changed.
1063 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1066 /* If we have moved tracks, we'll fudge the layer delta so that the
1067 region gets moved back onto layer 0 on its new track; this avoids
1068 confusion when dragging regions from non-zero layers onto different
1071 double this_delta_layer = delta_layer;
1072 if (delta_time_axis_view != 0) {
1073 this_delta_layer = - i->layer;
1076 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1078 int track_index = i->time_axis_view + this_delta_time_axis_view;
1079 assert(track_index >= 0);
1081 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1082 /* Track is in the Dropzone */
1084 i->time_axis_view = track_index;
1085 assert(i->time_axis_view >= (int) _time_axis_views.size());
1088 double yposition = 0;
1089 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1090 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1093 /* store index of each new playlist as a negative count, starting at -1 */
1095 if (pdz == playlist_dropzone_map.end()) {
1096 /* compute where this new track (which doesn't exist yet) will live
1099 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1101 /* How high is this region view ? */
1103 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1104 ArdourCanvas::Rect bbox;
1107 bbox = obbox.get ();
1110 last_track_bottom_edge += bbox.height();
1112 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1115 yposition = pdz->second;
1118 /* values are zero or negative, hence the use of min() */
1119 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1122 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1124 mrv->apply_note_range (60, 71, true);
1128 /* The TimeAxisView that this region is now over */
1129 TimeAxisView* current_tv = _time_axis_views[track_index];
1131 /* Ensure it is moved from stacked -> expanded if appropriate */
1132 if (current_tv->view()->layer_display() == Stacked) {
1133 current_tv->view()->set_layer_display (Expanded);
1136 /* We're only allowed to go -ve in layer on Expanded views */
1137 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1138 this_delta_layer = - i->layer;
1142 rv->set_height (current_tv->view()->child_height ());
1144 /* Update show/hidden status as the region view may have come from a hidden track,
1145 or have moved to one.
1147 if (current_tv->hidden ()) {
1148 rv->get_canvas_group()->hide ();
1150 rv->get_canvas_group()->show ();
1153 /* Update the DraggingView */
1154 i->time_axis_view = track_index;
1155 i->layer += this_delta_layer;
1158 _editor->mouse_brush_insert_region (rv, pending_region_position.frame);
1162 /* Get the y coordinate of the top of the track that this region is now over */
1163 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1165 /* And adjust for the layer that it should be on */
1166 StreamView* cv = current_tv->view ();
1167 switch (cv->layer_display ()) {
1171 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1174 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1178 /* need to get the parent of the regionview
1179 * canvas group and get its position in
1180 * equivalent coordinate space as the trackview
1181 * we are now dragging over.
1184 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1188 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1190 MidiStreamView* msv;
1191 if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
1192 mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
1197 /* Now move the region view */
1198 if (rv->region()->position_lock_style() == MusicTime) {
1199 double const last_qn = tmap.quarter_note_at_frame (rv->get_position());
1200 framepos_t const x_pos_music = tmap.frame_at_quarter_note (last_qn + qn_delta);
1202 rv->set_position (x_pos_music, 0);
1203 rv->move (0, y_delta);
1205 rv->move (x_delta, y_delta);
1208 } /* foreach region */
1210 _total_x_delta += x_delta;
1212 if (x_delta != 0 && !_brushing) {
1213 show_verbose_cursor_time (_last_position.frame);
1216 /* keep track of pointer movement */
1218 /* the pointer is currently over a time axis view */
1220 if (_last_pointer_time_axis_view < 0) {
1221 /* last motion event was not over a time axis view
1222 * or last y-movement out of the dropzone was not valid
1225 if (delta_time_axis_view < 0) {
1226 /* in the drop zone, moving up */
1228 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1229 * We do not use negative _last_pointer_time_axis_view because
1230 * the dropzone is "packed" (the actual track offset is ignored)
1232 * As opposed to the actual number
1233 * of elements in the dropzone (_ndropzone)
1234 * _pdropzone is not constrained. This is necessary
1235 * to allow moving multiple regions with y-distance
1238 * There can be 0 elements in the dropzone,
1239 * even though the drag-pointer is inside the DZ.
1242 * [ Audio-track, Midi-track, Audio-track, DZ ]
1243 * move regions from both audio tracks at the same time into the
1244 * DZ by grabbing the region in the bottom track.
1246 assert(current_pointer_time_axis_view >= 0);
1247 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1251 /* only move out of the zone if the movement is OK */
1252 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1253 assert(delta_time_axis_view < 0);
1254 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1255 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1256 * the current position can be calculated as follows:
1258 // a well placed oofus attack can still throw this off.
1259 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1260 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1263 /* last motion event was also over a time axis view */
1264 _last_pointer_time_axis_view += delta_time_axis_view;
1265 assert(_last_pointer_time_axis_view >= 0);
1270 /* the pointer is not over a time axis view */
1271 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1272 _pdropzone += delta_time_axis_view - delta_skip;
1273 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1276 _last_pointer_layer += delta_layer;
1280 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1282 if (_copy && first_move) {
1283 if (_x_constrained && !_brushing) {
1284 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1285 } else if (!_brushing) {
1286 _editor->begin_reversible_command (Operations::region_copy);
1287 } else if (_brushing) {
1288 _editor->begin_reversible_command (Operations::drag_region_brush);
1290 /* duplicate the regionview(s) and region(s) */
1292 list<DraggingView> new_regionviews;
1294 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1296 RegionView* rv = i->view;
1297 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1298 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1300 const boost::shared_ptr<const Region> original = rv->region();
1301 boost::shared_ptr<Region> region_copy;
1303 region_copy = RegionFactory::create (original, true);
1305 /* need to set this so that the drop zone code can work. This doesn't
1306 actually put the region into the playlist, but just sets a weak pointer
1309 region_copy->set_playlist (original->playlist());
1313 boost::shared_ptr<AudioRegion> audioregion_copy
1314 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1316 nrv = new AudioRegionView (*arv, audioregion_copy);
1318 boost::shared_ptr<MidiRegion> midiregion_copy
1319 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1320 nrv = new MidiRegionView (*mrv, midiregion_copy);
1325 nrv->get_canvas_group()->show ();
1326 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1328 /* swap _primary to the copy */
1330 if (rv == _primary) {
1334 /* ..and deselect the one we copied */
1336 rv->set_selected (false);
1339 if (!new_regionviews.empty()) {
1341 /* reflect the fact that we are dragging the copies */
1343 _views = new_regionviews;
1345 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1348 } else if (!_copy && first_move) {
1349 if (_x_constrained && !_brushing) {
1350 _editor->begin_reversible_command (_("fixed time region drag"));
1351 } else if (!_brushing) {
1352 _editor->begin_reversible_command (Operations::region_drag);
1353 } else if (_brushing) {
1354 _editor->begin_reversible_command (Operations::drag_region_brush);
1357 RegionMotionDrag::motion (event, first_move);
1361 RegionMotionDrag::finished (GdkEvent *, bool)
1363 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1364 if (!(*i)->view()) {
1368 if ((*i)->view()->layer_display() == Expanded) {
1369 (*i)->view()->set_layer_display (Stacked);
1375 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1377 RegionMotionDrag::finished (ev, movement_occurred);
1379 if (!movement_occurred) {
1383 if (was_double_click() && !_views.empty()) {
1384 DraggingView dv = _views.front();
1385 _editor->edit_region (dv.view);
1391 assert (!_views.empty ());
1393 /* We might have hidden region views so that they weren't visible during the drag
1394 (when they have been reparented). Now everything can be shown again, as region
1395 views are back in their track parent groups.
1397 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1398 i->view->get_canvas_group()->show ();
1401 bool const changed_position = (_last_position.frame != _primary->region()->position());
1402 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1426 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1428 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1431 TimeAxisView* tav = 0;
1433 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1434 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1435 uint32_t output_chan = region->n_channels();
1436 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1437 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1439 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1440 tav =_editor->axis_view_from_stripable (audio_tracks.front());
1442 ChanCount one_midi_port (DataType::MIDI, 1);
1443 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1444 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port,
1445 Config->get_strict_io () || Profile->get_mixbus (),
1446 boost::shared_ptr<ARDOUR::PluginInfo>(),
1447 (ARDOUR::Plugin::PresetRecord*) 0,
1448 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1449 tav = _editor->axis_view_from_stripable (midi_tracks.front());
1453 tav->set_height (original->current_height());
1456 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1459 return dynamic_cast<RouteTimeAxisView*> (tav);
1463 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, MusicFrame last_position, int32_t const ev_state)
1465 RegionSelection new_views;
1466 PlaylistSet modified_playlists;
1467 RouteTimeAxisView* new_time_axis_view = 0;
1468 framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1470 TempoMap& tmap (_editor->session()->tempo_map());
1471 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1472 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1475 /* all changes were made during motion event handlers */
1477 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1481 _editor->commit_reversible_command ();
1485 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1486 PlaylistMapping playlist_mapping;
1488 /* insert the regions into their new playlists */
1489 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1491 RouteTimeAxisView* dest_rtv = 0;
1493 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1497 MusicFrame where (0, 0);
1498 double quarter_note;
1500 if (changed_position && !_x_constrained) {
1501 where.set (i->view->region()->position() - drag_delta, 0);
1502 quarter_note = i->view->region()->quarter_note() - qn_delta;
1504 /* region has not moved - divisor will not affect musical pos */
1505 where.set (i->view->region()->position(), 0);
1506 quarter_note = i->view->region()->quarter_note();
1509 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1510 /* dragged to drop zone */
1512 PlaylistMapping::iterator pm;
1514 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1515 /* first region from this original playlist: create a new track */
1516 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1517 if(!new_time_axis_view) {
1521 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1522 dest_rtv = new_time_axis_view;
1524 /* we already created a new track for regions from this playlist, use it */
1525 dest_rtv = pm->second;
1528 /* destination time axis view is the one we dragged to */
1529 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1532 if (dest_rtv != 0) {
1533 RegionView* new_view;
1534 if (i->view == _primary && !_x_constrained) {
1535 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, last_position, last_pos_qn,
1536 modified_playlists, true);
1538 if (i->view->region()->position_lock_style() == AudioTime) {
1539 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1540 modified_playlists);
1542 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1543 modified_playlists, true);
1547 if (new_view != 0) {
1548 new_views.push_back (new_view);
1552 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1553 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1556 list<DraggingView>::const_iterator next = i;
1562 /* If we've created new regions either by copying or moving
1563 to a new track, we want to replace the old selection with the new ones
1566 if (new_views.size() > 0) {
1567 _editor->selection->set (new_views);
1570 /* write commands for the accumulated diffs for all our modified playlists */
1571 add_stateful_diff_commands_for_playlists (modified_playlists);
1573 _editor->commit_reversible_command ();
1577 RegionMoveDrag::finished_no_copy (
1578 bool const changed_position,
1579 bool const changed_tracks,
1580 MusicFrame last_position,
1581 int32_t const ev_state
1584 RegionSelection new_views;
1585 PlaylistSet modified_playlists;
1586 PlaylistSet frozen_playlists;
1587 set<RouteTimeAxisView*> views_to_update;
1588 RouteTimeAxisView* new_time_axis_view = 0;
1589 framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1591 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1592 PlaylistMapping playlist_mapping;
1594 TempoMap& tmap (_editor->session()->tempo_map());
1595 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1596 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1598 std::set<boost::shared_ptr<const Region> > uniq;
1599 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1601 RegionView* rv = i->view;
1602 RouteTimeAxisView* dest_rtv = 0;
1604 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1609 if (uniq.find (rv->region()) != uniq.end()) {
1610 /* prevent duplicate moves when selecting regions from shared playlists */
1614 uniq.insert(rv->region());
1616 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1617 /* dragged to drop zone */
1619 PlaylistMapping::iterator pm;
1621 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1622 /* first region from this original playlist: create a new track */
1623 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1624 if(!new_time_axis_view) { // New track creation failed
1628 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1629 dest_rtv = new_time_axis_view;
1631 /* we already created a new track for regions from this playlist, use it */
1632 dest_rtv = pm->second;
1636 /* destination time axis view is the one we dragged to */
1637 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1642 double const dest_layer = i->layer;
1644 views_to_update.insert (dest_rtv);
1646 MusicFrame where (0, 0);
1647 double quarter_note;
1649 if (changed_position && !_x_constrained) {
1650 where.set (rv->region()->position() - drag_delta, 0);
1651 quarter_note = i->view->region()->quarter_note() - qn_delta;
1653 where.set (rv->region()->position(), 0);
1654 quarter_note = i->view->region()->quarter_note();
1657 if (changed_tracks) {
1659 /* insert into new playlist */
1660 RegionView* new_view;
1661 if (rv == _primary && !_x_constrained) {
1662 new_view = insert_region_into_playlist (
1663 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, last_position, last_pos_qn,
1664 modified_playlists, true
1667 if (rv->region()->position_lock_style() == AudioTime) {
1669 new_view = insert_region_into_playlist (
1670 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1674 new_view = insert_region_into_playlist (
1675 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1676 modified_playlists, true
1681 if (new_view == 0) {
1686 new_views.push_back (new_view);
1688 /* remove from old playlist */
1690 /* the region that used to be in the old playlist is not
1691 moved to the new one - we use a copy of it. as a result,
1692 any existing editor for the region should no longer be
1695 rv->hide_region_editor();
1698 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1702 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1704 /* this movement may result in a crossfade being modified, or a layering change,
1705 so we need to get undo data from the playlist as well as the region.
1708 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1710 playlist->clear_changes ();
1713 rv->region()->clear_changes ();
1716 motion on the same track. plonk the previously reparented region
1717 back to its original canvas group (its streamview).
1718 No need to do anything for copies as they are fake regions which will be deleted.
1721 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1722 rv->get_canvas_group()->set_y_position (i->initial_y);
1725 /* just change the model */
1726 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1727 playlist->set_layer (rv->region(), dest_layer);
1730 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1732 r = frozen_playlists.insert (playlist);
1735 playlist->freeze ();
1737 if (rv == _primary) {
1738 rv->region()->set_position (where.frame, last_position.division);
1740 if (rv->region()->position_lock_style() == AudioTime) {
1741 /* move by frame offset */
1742 rv->region()->set_position (where.frame, 0);
1744 /* move by music offset */
1745 rv->region()->set_position_music (rv->region()->quarter_note() - qn_delta);
1748 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1751 if (changed_tracks) {
1753 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1754 was selected in all of them, then removing it from a playlist will have removed all
1755 trace of it from _views (i.e. there were N regions selected, we removed 1,
1756 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1757 corresponding regionview, and _views is now empty).
1759 This could have invalidated any and all iterators into _views.
1761 The heuristic we use here is: if the region selection is empty, break out of the loop
1762 here. if the region selection is not empty, then restart the loop because we know that
1763 we must have removed at least the region(view) we've just been working on as well as any
1764 that we processed on previous iterations.
1766 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1767 we can just iterate.
1771 if (_views.empty()) {
1782 /* If we've created new regions either by copying or moving
1783 to a new track, we want to replace the old selection with the new ones
1786 if (new_views.size() > 0) {
1787 _editor->selection->set (new_views);
1790 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1794 /* write commands for the accumulated diffs for all our modified playlists */
1795 add_stateful_diff_commands_for_playlists (modified_playlists);
1796 /* applies to _brushing */
1797 _editor->commit_reversible_command ();
1799 /* We have futzed with the layering of canvas items on our streamviews.
1800 If any region changed layer, this will have resulted in the stream
1801 views being asked to set up their region views, and all will be well.
1802 If not, we might now have badly-ordered region views. Ask the StreamViews
1803 involved to sort themselves out, just in case.
1806 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1807 (*i)->view()->playlist_layered ((*i)->track ());
1811 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1812 * @param region Region to remove.
1813 * @param playlist playlist To remove from.
1814 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1815 * that clear_changes () is only called once per playlist.
1818 RegionMoveDrag::remove_region_from_playlist (
1819 boost::shared_ptr<Region> region,
1820 boost::shared_ptr<Playlist> playlist,
1821 PlaylistSet& modified_playlists
1824 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1827 playlist->clear_changes ();
1830 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1834 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1835 * clearing the playlist's diff history first if necessary.
1836 * @param region Region to insert.
1837 * @param dest_rtv Destination RouteTimeAxisView.
1838 * @param dest_layer Destination layer.
1839 * @param where Destination position.
1840 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1841 * that clear_changes () is only called once per playlist.
1842 * @return New RegionView, or 0 if no insert was performed.
1845 RegionMoveDrag::insert_region_into_playlist (
1846 boost::shared_ptr<Region> region,
1847 RouteTimeAxisView* dest_rtv,
1850 double quarter_note,
1851 PlaylistSet& modified_playlists,
1855 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1856 if (!dest_playlist) {
1860 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1861 _new_region_view = 0;
1862 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1864 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1865 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1867 dest_playlist->clear_changes ();
1870 dest_playlist->add_region (region, where.frame, 1.0, false, where.division, quarter_note, true);
1872 dest_playlist->add_region (region, where.frame, 1.0, false, where.division);
1875 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1876 dest_playlist->set_layer (region, dest_layer);
1881 assert (_new_region_view);
1883 return _new_region_view;
1887 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1889 _new_region_view = rv;
1893 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1895 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1896 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1898 _editor->session()->add_command (c);
1907 RegionMoveDrag::aborted (bool movement_occurred)
1911 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1912 list<DraggingView>::const_iterator next = i;
1921 RegionMotionDrag::aborted (movement_occurred);
1926 RegionMotionDrag::aborted (bool)
1928 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1930 StreamView* sview = (*i)->view();
1933 if (sview->layer_display() == Expanded) {
1934 sview->set_layer_display (Stacked);
1939 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1940 RegionView* rv = i->view;
1941 TimeAxisView* tv = &(rv->get_time_axis_view ());
1942 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1944 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1945 rv->get_canvas_group()->set_y_position (0);
1947 rv->move (-_total_x_delta, 0);
1948 rv->set_height (rtv->view()->child_height ());
1952 /** @param b true to brush, otherwise false.
1953 * @param c true to make copies of the regions being moved, otherwise false.
1955 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1956 : RegionMotionDrag (e, i, p, v, b)
1958 , _new_region_view (0)
1960 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1963 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1964 if (rtv && rtv->is_track()) {
1965 speed = rtv->track()->speed ();
1968 _last_position = MusicFrame (static_cast<framepos_t> (_primary->region()->position() / speed), 0);
1972 RegionMoveDrag::setup_pointer_frame_offset ()
1974 _pointer_frame_offset = raw_grab_frame() - _last_position.frame;
1977 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1978 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1980 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1982 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1983 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1985 _primary = v->view()->create_region_view (r, false, false);
1987 _primary->get_canvas_group()->show ();
1988 _primary->set_position (pos, 0);
1989 _views.push_back (DraggingView (_primary, this, v));
1991 _last_position = MusicFrame (pos, 0);
1993 _item = _primary->get_canvas_group ();
1997 RegionInsertDrag::finished (GdkEvent * event, bool)
1999 int pos = _views.front().time_axis_view;
2000 assert(pos >= 0 && pos < (int)_time_axis_views.size());
2002 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
2004 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
2005 _primary->get_canvas_group()->set_y_position (0);
2007 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
2009 _editor->begin_reversible_command (Operations::insert_region);
2010 playlist->clear_changes ();
2011 _editor->snap_to_with_modifier (_last_position, event);
2013 playlist->add_region (_primary->region (), _last_position.frame, 1.0, false, _last_position.division);
2015 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
2016 if (Config->get_edit_mode() == Ripple) {
2017 playlist->ripple (_last_position.frame, _primary->region()->length(), _primary->region());
2020 _editor->session()->add_command (new StatefulDiffCommand (playlist));
2021 _editor->commit_reversible_command ();
2029 RegionInsertDrag::aborted (bool)
2036 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2037 : RegionMoveDrag (e, i, p, v, false, false)
2039 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
2042 struct RegionSelectionByPosition {
2043 bool operator() (RegionView*a, RegionView* b) {
2044 return a->region()->position () < b->region()->position();
2049 RegionSpliceDrag::motion (GdkEvent* event, bool)
2051 /* Which trackview is this ? */
2053 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2054 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2056 /* The region motion is only processed if the pointer is over
2060 if (!tv || !tv->is_track()) {
2061 /* To make sure we hide the verbose canvas cursor when the mouse is
2062 not held over an audio track.
2064 _editor->verbose_cursor()->hide ();
2067 _editor->verbose_cursor()->show ();
2072 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2078 RegionSelection copy;
2079 _editor->selection->regions.by_position(copy);
2081 framepos_t const pf = adjusted_current_frame (event);
2083 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2085 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2091 boost::shared_ptr<Playlist> playlist;
2093 if ((playlist = atv->playlist()) == 0) {
2097 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2102 if (pf < (*i)->region()->last_frame() + 1) {
2106 if (pf > (*i)->region()->first_frame()) {
2112 playlist->shuffle ((*i)->region(), dir);
2117 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2119 RegionMoveDrag::finished (event, movement_occurred);
2123 RegionSpliceDrag::aborted (bool)
2133 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2136 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2138 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2139 RegionSelection to_ripple;
2140 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2141 if ((*i)->position() >= where) {
2142 to_ripple.push_back (rtv->view()->find_view(*i));
2146 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2147 if (!exclude.contains (*i)) {
2148 // the selection has already been added to _views
2150 if (drag_in_progress) {
2151 // do the same things that RegionMotionDrag::motion does when
2152 // first_move is true, for the region views that we're adding
2153 // to _views this time
2156 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2157 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2158 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2159 rvg->reparent (_editor->_drag_motion_group);
2161 // we only need to move in the y direction
2162 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2167 _views.push_back (DraggingView (*i, this, tav));
2173 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2176 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2177 // we added all the regions after the selection
2179 std::list<DraggingView>::iterator to_erase = i++;
2180 if (!_editor->selection->regions.contains (to_erase->view)) {
2181 // restore the non-selected regions to their original playlist & positions,
2182 // and then ripple them back by the length of the regions that were dragged away
2183 // do the same things as RegionMotionDrag::aborted
2185 RegionView *rv = to_erase->view;
2186 TimeAxisView* tv = &(rv->get_time_axis_view ());
2187 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2190 // plonk them back onto their own track
2191 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2192 rv->get_canvas_group()->set_y_position (0);
2196 // move the underlying region to match the view
2197 rv->region()->set_position (rv->region()->position() + amount);
2199 // restore the view to match the underlying region's original position
2200 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2203 rv->set_height (rtv->view()->child_height ());
2204 _views.erase (to_erase);
2210 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2212 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2214 return allow_moves_across_tracks;
2222 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2223 : RegionMoveDrag (e, i, p, v, false, false)
2225 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2226 // compute length of selection
2227 RegionSelection selected_regions = _editor->selection->regions;
2228 selection_length = selected_regions.end_frame() - selected_regions.start();
2230 // we'll only allow dragging to another track in ripple mode if all the regions
2231 // being dragged start off on the same track
2232 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2235 exclude = new RegionList;
2236 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2237 exclude->push_back((*i)->region());
2240 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2241 RegionSelection copy;
2242 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2244 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2245 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2247 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2248 // find ripple start point on each applicable playlist
2249 RegionView *first_selected_on_this_track = NULL;
2250 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2251 if ((*i)->region()->playlist() == (*pi)) {
2252 // region is on this playlist - it's the first, because they're sorted
2253 first_selected_on_this_track = *i;
2257 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2258 add_all_after_to_views (
2259 &first_selected_on_this_track->get_time_axis_view(),
2260 first_selected_on_this_track->region()->position(),
2261 selected_regions, false);
2264 if (allow_moves_across_tracks) {
2265 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2273 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2275 /* Which trackview is this ? */
2277 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2278 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2280 /* The region motion is only processed if the pointer is over
2284 if (!tv || !tv->is_track()) {
2285 /* To make sure we hide the verbose canvas cursor when the mouse is
2286 not held over an audiotrack.
2288 _editor->verbose_cursor()->hide ();
2292 framepos_t where = adjusted_current_frame (event);
2293 assert (where >= 0);
2294 MusicFrame after (0, 0);
2295 double delta = compute_x_delta (event, &after);
2297 framecnt_t amount = _editor->pixel_to_sample (delta);
2299 if (allow_moves_across_tracks) {
2300 // all the originally selected regions were on the same track
2302 framecnt_t adjust = 0;
2303 if (prev_tav && tv != prev_tav) {
2304 // dragged onto a different track
2305 // remove the unselected regions from _views, restore them to their original positions
2306 // and add the regions after the drop point on the new playlist to _views instead.
2307 // undo the effect of rippling the previous playlist, and include the effect of removing
2308 // the dragged region(s) from this track
2310 remove_unselected_from_views (prev_amount, false);
2311 // ripple previous playlist according to the regions that have been removed onto the new playlist
2312 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2315 // move just the selected regions
2316 RegionMoveDrag::motion(event, first_move);
2318 // ensure that the ripple operation on the new playlist inserts selection_length time
2319 adjust = selection_length;
2320 // ripple the new current playlist
2321 tv->playlist()->ripple (where, amount+adjust, exclude);
2323 // add regions after point where drag entered this track to subsequent ripples
2324 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2327 // motion on same track
2328 RegionMoveDrag::motion(event, first_move);
2332 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2333 prev_position = where;
2335 // selection encompasses multiple tracks - just drag
2336 // cross-track drags are forbidden
2337 RegionMoveDrag::motion(event, first_move);
2340 if (!_x_constrained) {
2341 prev_amount += amount;
2344 _last_position = after;
2348 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2350 if (!movement_occurred) {
2354 if (was_double_click() && !_views.empty()) {
2355 DraggingView dv = _views.front();
2356 _editor->edit_region (dv.view);
2362 _editor->begin_reversible_command(_("Ripple drag"));
2364 // remove the regions being rippled from the dragging view, updating them to
2365 // their new positions
2366 remove_unselected_from_views (prev_amount, true);
2368 if (allow_moves_across_tracks) {
2370 // if regions were dragged across tracks, we've rippled any later
2371 // regions on the track the regions were dragged off, so we need
2372 // to add the original track to the undo record
2373 orig_tav->playlist()->clear_changes();
2374 vector<Command*> cmds;
2375 orig_tav->playlist()->rdiff (cmds);
2376 _editor->session()->add_commands (cmds);
2378 if (prev_tav && prev_tav != orig_tav) {
2379 prev_tav->playlist()->clear_changes();
2380 vector<Command*> cmds;
2381 prev_tav->playlist()->rdiff (cmds);
2382 _editor->session()->add_commands (cmds);
2385 // selection spanned multiple tracks - all will need adding to undo record
2387 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2388 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2390 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2391 (*pi)->clear_changes();
2392 vector<Command*> cmds;
2393 (*pi)->rdiff (cmds);
2394 _editor->session()->add_commands (cmds);
2398 // other modified playlists are added to undo by RegionMoveDrag::finished()
2399 RegionMoveDrag::finished (event, movement_occurred);
2400 _editor->commit_reversible_command();
2404 RegionRippleDrag::aborted (bool movement_occurred)
2406 RegionMoveDrag::aborted (movement_occurred);
2411 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2413 _view (dynamic_cast<MidiTimeAxisView*> (v))
2415 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2421 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2424 _editor->begin_reversible_command (_("create region"));
2425 _region = add_midi_region (_view, false);
2426 _view->playlist()->freeze ();
2429 framepos_t const f = adjusted_current_frame (event);
2430 if (f < grab_frame()) {
2431 _region->set_initial_position (f);
2434 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2435 so that if this region is duplicated, its duplicate starts on
2436 a snap point rather than 1 frame after a snap point. Otherwise things get
2437 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2438 place snapped notes at the start of the region.
2441 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2442 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2448 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2450 if (!movement_occurred) {
2451 add_midi_region (_view, true);
2453 _view->playlist()->thaw ();
2454 _editor->commit_reversible_command();
2459 RegionCreateDrag::aborted (bool)
2462 _view->playlist()->thaw ();
2468 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2473 , _was_selected (false)
2476 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2480 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2482 Gdk::Cursor* cursor;
2483 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2485 float x_fraction = cnote->mouse_x_fraction ();
2487 if (x_fraction > 0.0 && x_fraction < 0.25) {
2488 cursor = _editor->cursors()->left_side_trim;
2491 cursor = _editor->cursors()->right_side_trim;
2495 Drag::start_grab (event, cursor);
2497 region = &cnote->region_view();
2500 temp = region->snap_to_pixel (cnote->x0 (), true);
2501 _snap_delta = temp - cnote->x0 ();
2505 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2510 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2511 if (ms.size() > 1) {
2512 /* has to be relative, may make no sense otherwise */
2516 if (!(_was_selected = cnote->selected())) {
2518 /* tertiary-click means extend selection - we'll do that on button release,
2519 so don't add it here, because otherwise we make it hard to figure
2520 out the "extend-to" range.
2523 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2526 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2529 region->note_selected (cnote, true);
2531 _editor->get_selection().clear_points();
2532 region->unique_select (cnote);
2539 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2541 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2543 _editor->begin_reversible_command (_("resize notes"));
2545 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2546 MidiRegionSelection::iterator next;
2549 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2551 mrv->begin_resizing (at_front);
2557 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2558 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2560 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2564 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2566 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2567 if (_editor->snap_mode () != SnapOff) {
2571 if (_editor->snap_mode () == SnapOff) {
2573 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2574 if (apply_snap_delta) {
2580 if (apply_snap_delta) {
2584 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2590 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2592 if (!movement_occurred) {
2593 /* no motion - select note */
2594 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2595 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2596 _editor->current_mouse_mode() == Editing::MouseDraw) {
2598 bool changed = false;
2600 if (_was_selected) {
2601 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2603 region->note_deselected (cnote);
2606 _editor->get_selection().clear_points();
2607 region->unique_select (cnote);
2611 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2612 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2614 if (!extend && !add && region->selection_size() > 1) {
2615 _editor->get_selection().clear_points();
2616 region->unique_select (cnote);
2618 } else if (extend) {
2619 region->note_selected (cnote, true, true);
2622 /* it was added during button press */
2628 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2629 _editor->commit_reversible_selection_op();
2636 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2637 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2638 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2640 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2643 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2645 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2646 if (_editor->snap_mode () != SnapOff) {
2650 if (_editor->snap_mode () == SnapOff) {
2652 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2653 if (apply_snap_delta) {
2659 if (apply_snap_delta) {
2663 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2667 _editor->commit_reversible_command ();
2671 NoteResizeDrag::aborted (bool)
2673 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2674 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2675 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2677 mrv->abort_resizing ();
2682 AVDraggingView::AVDraggingView (RegionView* v)
2685 initial_position = v->region()->position ();
2688 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2691 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2694 TrackViewList empty;
2696 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2697 std::list<RegionView*> views = rs.by_layer();
2700 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2701 RegionView* rv = (*i);
2702 if (!rv->region()->video_locked()) {
2705 if (rv->region()->locked()) {
2708 _views.push_back (AVDraggingView (rv));
2713 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2715 Drag::start_grab (event);
2716 if (_editor->session() == 0) {
2720 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2726 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2730 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2731 _max_backwards_drag = (
2732 ARDOUR_UI::instance()->video_timeline->get_duration()
2733 + ARDOUR_UI::instance()->video_timeline->get_offset()
2734 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2737 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2738 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2739 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2742 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2745 Timecode::Time timecode;
2746 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2747 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);
2748 show_verbose_cursor_text (buf);
2752 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2754 if (_editor->session() == 0) {
2757 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2761 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2765 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2766 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2768 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2769 dt = - _max_backwards_drag;
2772 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2773 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2775 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2776 RegionView* rv = i->view;
2777 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2780 rv->region()->clear_changes ();
2781 rv->region()->suspend_property_changes();
2783 rv->region()->set_position(i->initial_position + dt);
2784 rv->region_changed(ARDOUR::Properties::position);
2787 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2788 Timecode::Time timecode;
2789 Timecode::Time timediff;
2791 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2792 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2793 snprintf (buf, sizeof (buf),
2794 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2795 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2796 , _("Video Start:"),
2797 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2799 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2801 show_verbose_cursor_text (buf);
2805 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2807 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2814 if (!movement_occurred || ! _editor->session()) {
2818 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2820 _editor->begin_reversible_command (_("Move Video"));
2822 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2823 ARDOUR_UI::instance()->video_timeline->save_undo();
2824 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2825 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2827 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2828 i->view->drag_end();
2829 i->view->region()->resume_property_changes ();
2831 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2834 _editor->session()->maybe_update_session_range(
2835 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2836 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2840 _editor->commit_reversible_command ();
2844 VideoTimeLineDrag::aborted (bool)
2846 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2849 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2850 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2852 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2853 i->view->region()->resume_property_changes ();
2854 i->view->region()->set_position(i->initial_position);
2858 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2859 : RegionDrag (e, i, p, v)
2860 , _operation (StartTrim)
2861 , _preserve_fade_anchor (preserve_fade_anchor)
2862 , _jump_position_when_done (false)
2864 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2868 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2871 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2872 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2874 if (tv && tv->is_track()) {
2875 speed = tv->track()->speed();
2878 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2879 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2880 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2882 framepos_t const pf = adjusted_current_frame (event);
2883 setup_snap_delta (MusicFrame(region_start, 0));
2885 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2886 /* Move the contents of the region around without changing the region bounds */
2887 _operation = ContentsTrim;
2888 Drag::start_grab (event, _editor->cursors()->trimmer);
2890 /* These will get overridden for a point trim.*/
2891 if (pf < (region_start + region_length/2)) {
2892 /* closer to front */
2893 _operation = StartTrim;
2894 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2895 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2897 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2901 _operation = EndTrim;
2902 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2903 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2905 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2909 /* jump trim disabled for now
2910 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2911 _jump_position_when_done = true;
2915 switch (_operation) {
2917 show_verbose_cursor_time (region_start);
2920 show_verbose_cursor_duration (region_start, region_end);
2923 show_verbose_cursor_time (pf);
2927 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2928 i->view->region()->suspend_property_changes ();
2933 TrimDrag::motion (GdkEvent* event, bool first_move)
2935 RegionView* rv = _primary;
2938 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2939 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2940 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2941 frameoffset_t frame_delta = 0;
2943 if (tv && tv->is_track()) {
2944 speed = tv->track()->speed();
2946 MusicFrame adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2947 framecnt_t dt = adj_frame.frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2953 switch (_operation) {
2955 trim_type = "Region start trim";
2958 trim_type = "Region end trim";
2961 trim_type = "Region content trim";
2968 _editor->begin_reversible_command (trim_type);
2970 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2971 RegionView* rv = i->view;
2972 rv->region()->playlist()->clear_owned_changes ();
2974 if (_operation == StartTrim) {
2975 rv->trim_front_starting ();
2978 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2981 arv->temporarily_hide_envelope ();
2985 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2986 insert_result = _editor->motion_frozen_playlists.insert (pl);
2988 if (insert_result.second) {
2994 bool non_overlap_trim = false;
2996 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2997 non_overlap_trim = true;
3000 /* contstrain trim to fade length */
3001 if (_preserve_fade_anchor) {
3002 switch (_operation) {
3004 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3005 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3007 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3008 if (ar->locked()) continue;
3009 framecnt_t len = ar->fade_in()->back()->when;
3010 if (len < dt) dt = min(dt, len);
3014 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3015 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3017 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3018 if (ar->locked()) continue;
3019 framecnt_t len = ar->fade_out()->back()->when;
3020 if (len < -dt) dt = max(dt, -len);
3028 switch (_operation) {
3030 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3031 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
3032 , adj_frame.division);
3034 if (changed && _preserve_fade_anchor) {
3035 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3037 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3038 framecnt_t len = ar->fade_in()->back()->when;
3039 framecnt_t diff = ar->first_frame() - i->initial_position;
3040 framepos_t new_length = len - diff;
3041 i->anchored_fade_length = min (ar->length(), new_length);
3042 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
3043 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
3050 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3051 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, adj_frame.division);
3052 if (changed && _preserve_fade_anchor) {
3053 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3055 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3056 framecnt_t len = ar->fade_out()->back()->when;
3057 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
3058 framepos_t new_length = len + diff;
3059 i->anchored_fade_length = min (ar->length(), new_length);
3060 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
3061 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3069 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3071 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3072 i->view->move_contents (frame_delta);
3078 switch (_operation) {
3080 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3083 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3086 // show_verbose_cursor_time (frame_delta);
3092 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3094 if (movement_occurred) {
3095 motion (event, false);
3097 if (_operation == StartTrim) {
3098 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3100 /* This must happen before the region's StatefulDiffCommand is created, as it may
3101 `correct' (ahem) the region's _start from being negative to being zero. It
3102 needs to be zero in the undo record.
3104 i->view->trim_front_ending ();
3106 if (_preserve_fade_anchor) {
3107 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3109 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3110 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3111 ar->set_fade_in_length(i->anchored_fade_length);
3112 ar->set_fade_in_active(true);
3115 if (_jump_position_when_done) {
3116 i->view->region()->set_position (i->initial_position);
3119 } else if (_operation == EndTrim) {
3120 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3121 if (_preserve_fade_anchor) {
3122 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3124 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3125 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3126 ar->set_fade_out_length(i->anchored_fade_length);
3127 ar->set_fade_out_active(true);
3130 if (_jump_position_when_done) {
3131 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3136 if (!_editor->selection->selected (_primary)) {
3137 _primary->thaw_after_trim ();
3140 set<boost::shared_ptr<Playlist> > diffed_playlists;
3142 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3143 i->view->thaw_after_trim ();
3144 i->view->enable_display (true);
3146 /* Trimming one region may affect others on the playlist, so we need
3147 to get undo Commands from the whole playlist rather than just the
3148 region. Use diffed_playlists to make sure we don't diff a given
3149 playlist more than once.
3151 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3152 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3153 vector<Command*> cmds;
3155 _editor->session()->add_commands (cmds);
3156 diffed_playlists.insert (p);
3161 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3165 _editor->motion_frozen_playlists.clear ();
3166 _editor->commit_reversible_command();
3169 /* no mouse movement */
3170 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false).frame) {
3171 _editor->point_trim (event, adjusted_current_frame (event));
3175 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3176 i->view->region()->resume_property_changes ();
3181 TrimDrag::aborted (bool movement_occurred)
3183 /* Our motion method is changing model state, so use the Undo system
3184 to cancel. Perhaps not ideal, as this will leave an Undo point
3185 behind which may be slightly odd from the user's point of view.
3189 finished (&ev, true);
3191 if (movement_occurred) {
3192 _editor->session()->undo (1);
3195 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3196 i->view->region()->resume_property_changes ();
3201 TrimDrag::setup_pointer_frame_offset ()
3203 list<DraggingView>::iterator i = _views.begin ();
3204 while (i != _views.end() && i->view != _primary) {
3208 if (i == _views.end()) {
3212 switch (_operation) {
3214 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3217 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3224 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3227 , _old_snap_type (e->snap_type())
3228 , _old_snap_mode (e->snap_mode())
3231 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3232 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3234 _real_section = &_marker->meter();
3239 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3241 Drag::start_grab (event, cursor);
3242 show_verbose_cursor_time (adjusted_current_frame(event));
3246 MeterMarkerDrag::setup_pointer_frame_offset ()
3248 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3252 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3255 // create a dummy marker to catch events, then hide it.
3258 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3260 _marker = new MeterMarker (
3262 *_editor->meter_group,
3263 UIConfiguration::instance().color ("meter marker"),
3265 *new MeterSection (_marker->meter())
3268 /* use the new marker for the grab */
3269 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3272 TempoMap& map (_editor->session()->tempo_map());
3273 /* get current state */
3274 before_state = &map.get_state();
3277 _editor->begin_reversible_command (_("move meter mark"));
3279 _editor->begin_reversible_command (_("copy meter mark"));
3281 Timecode::BBT_Time bbt = _real_section->bbt();
3283 /* we can't add a meter where one currently exists */
3284 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3289 const double beat = map.beat_at_bbt (bbt);
3290 const framepos_t frame = map.frame_at_beat (beat);
3291 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3292 , beat, bbt, frame, _real_section->position_lock_style());
3293 if (!_real_section) {
3299 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3300 if (_real_section->position_lock_style() != AudioTime) {
3301 _editor->set_snap_to (SnapToBar);
3302 _editor->set_snap_mode (SnapNormal);
3306 framepos_t pf = adjusted_current_frame (event);
3308 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3309 /* never snap to music for audio locked */
3310 pf = adjusted_current_frame (event, false);
3313 _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3315 /* fake marker meeds to stay under the mouse, unlike the real one. */
3316 _marker->set_position (adjusted_current_frame (event, false));
3318 show_verbose_cursor_time (_real_section->frame());
3322 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3324 if (!movement_occurred) {
3325 if (was_double_click()) {
3326 _editor->edit_meter_marker (*_marker);
3331 /* reinstate old snap setting */
3332 _editor->set_snap_to (_old_snap_type);
3333 _editor->set_snap_mode (_old_snap_mode);
3335 TempoMap& map (_editor->session()->tempo_map());
3337 XMLNode &after = map.get_state();
3338 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3339 _editor->commit_reversible_command ();
3341 // delete the dummy marker we used for visual representation while moving.
3342 // a new visual marker will show up automatically.
3347 MeterMarkerDrag::aborted (bool moved)
3349 _marker->set_position (_marker->meter().frame ());
3351 /* reinstate old snap setting */
3352 _editor->set_snap_to (_old_snap_type);
3353 _editor->set_snap_mode (_old_snap_mode);
3355 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3356 // delete the dummy marker we used for visual representation while moving.
3357 // a new visual marker will show up automatically.
3362 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3365 , _grab_bpm (120.0, 4.0)
3369 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3371 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3372 _real_section = &_marker->tempo();
3373 _movable = !_real_section->initial();
3374 _grab_bpm = Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), _real_section->end_note_types_per_minute());
3375 _grab_qn = _real_section->pulse() * 4.0;
3380 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3382 Drag::start_grab (event, cursor);
3383 if (!_real_section->active()) {
3384 show_verbose_cursor_text (_("inactive"));
3386 show_verbose_cursor_time (adjusted_current_frame (event));
3391 TempoMarkerDrag::setup_pointer_frame_offset ()
3393 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3397 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3399 if (!_real_section->active()) {
3402 TempoMap& map (_editor->session()->tempo_map());
3406 // mvc drag - create a dummy marker to catch events, hide it.
3409 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3411 TempoSection section (_marker->tempo());
3413 _marker = new TempoMarker (
3415 *_editor->tempo_group,
3416 UIConfiguration::instance().color ("tempo marker"),
3418 *new TempoSection (_marker->tempo())
3421 /* use the new marker for the grab */
3422 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3425 /* get current state */
3426 before_state = &map.get_state();
3429 _editor->begin_reversible_command (_("move tempo mark"));
3432 const Tempo tempo (_marker->tempo());
3433 const framepos_t frame = adjusted_current_frame (event) + 1;
3435 _editor->begin_reversible_command (_("copy tempo mark"));
3437 if (_real_section->position_lock_style() == MusicTime) {
3438 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3439 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, MusicTime);
3441 _real_section = map.add_tempo (tempo, 0.0, frame, AudioTime);
3444 if (!_real_section) {
3451 if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
3452 double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3454 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), true);
3455 strs << "end:" << fixed << setprecision(3) << new_bpm;
3456 show_verbose_cursor_text (strs.str());
3458 } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3459 /* use vertical movement to alter tempo .. should be log */
3460 double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3462 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), false);
3463 strs << "start:" << fixed << setprecision(3) << new_bpm;
3464 show_verbose_cursor_text (strs.str());
3466 } else if (_movable && !_real_section->locked_to_meter()) {
3469 if (_editor->snap_musical()) {
3470 /* we can't snap to a grid that we are about to move.
3471 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3473 pf = adjusted_current_frame (event, false);
3475 pf = adjusted_current_frame (event);
3478 /* snap to beat is 1, snap to bar is -1 (sorry) */
3479 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3481 map.gui_set_tempo_position (_real_section, pf, sub_num);
3483 show_verbose_cursor_time (_real_section->frame());
3485 _marker->set_position (adjusted_current_frame (event, false));
3489 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3491 if (!_real_section->active()) {
3494 if (!movement_occurred) {
3495 if (was_double_click()) {
3496 _editor->edit_tempo_marker (*_marker);
3501 TempoMap& map (_editor->session()->tempo_map());
3503 XMLNode &after = map.get_state();
3504 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3505 _editor->commit_reversible_command ();
3507 // delete the dummy marker we used for visual representation while moving.
3508 // a new visual marker will show up automatically.
3513 TempoMarkerDrag::aborted (bool moved)
3515 _marker->set_position (_marker->tempo().frame());
3517 TempoMap& map (_editor->session()->tempo_map());
3518 map.set_state (*before_state, Stateful::current_state_version);
3519 // delete the dummy (hidden) marker we used for events while moving.
3524 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3530 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3535 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3537 Drag::start_grab (event, cursor);
3538 TempoMap& map (_editor->session()->tempo_map());
3539 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3540 _editor->tempo_curve_selected (_tempo, true);
3543 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3544 show_verbose_cursor_text (sstr.str());
3548 BBTRulerDrag::setup_pointer_frame_offset ()
3550 TempoMap& map (_editor->session()->tempo_map());
3551 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3552 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3555 if (divisions > 0) {
3556 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3558 /* while it makes some sense for the user to determine the division to 'grab',
3559 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3560 and the result over steep tempo curves. Use sixteenths.
3562 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3565 _grab_qn = map.quarter_note_at_beat (beat);
3567 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3572 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3574 TempoMap& map (_editor->session()->tempo_map());
3577 /* get current state */
3578 before_state = &map.get_state();
3579 _editor->begin_reversible_command (_("stretch tempo"));
3584 if (_editor->snap_musical()) {
3585 pf = adjusted_current_frame (event, false);
3587 pf = adjusted_current_frame (event);
3590 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3591 /* adjust previous tempo to match pointer frame */
3592 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3595 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3596 show_verbose_cursor_text (sstr.str());
3600 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3602 if (!movement_occurred) {
3606 TempoMap& map (_editor->session()->tempo_map());
3608 XMLNode &after = map.get_state();
3609 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3610 _editor->commit_reversible_command ();
3611 _editor->tempo_curve_selected (_tempo, false);
3615 BBTRulerDrag::aborted (bool moved)
3618 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3622 TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
3628 , _drag_valid (true)
3631 DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
3636 TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3638 Drag::start_grab (event, cursor);
3639 TempoMap& map (_editor->session()->tempo_map());
3640 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3642 _next_tempo = map.next_tempo_section (_tempo);
3644 if (!map.next_tempo_section (_next_tempo)) {
3645 _drag_valid = false;
3646 finished (event, false);
3650 _editor->tempo_curve_selected (_tempo, true);
3651 _editor->tempo_curve_selected (_next_tempo, true);
3654 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3655 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3656 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3657 show_verbose_cursor_text (sstr.str());
3659 _drag_valid = false;
3662 _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
3666 TempoTwistDrag::setup_pointer_frame_offset ()
3668 TempoMap& map (_editor->session()->tempo_map());
3669 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3670 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3673 if (divisions > 0) {
3674 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3676 /* while it makes some sense for the user to determine the division to 'grab',
3677 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3678 and the result over steep tempo curves. Use sixteenths.
3680 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3683 _grab_qn = map.quarter_note_at_beat (beat);
3685 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3690 TempoTwistDrag::motion (GdkEvent* event, bool first_move)
3693 if (!_next_tempo || !_drag_valid) {
3697 TempoMap& map (_editor->session()->tempo_map());
3700 /* get current state */
3701 before_state = &map.get_state();
3702 _editor->begin_reversible_command (_("twist tempo"));
3707 if (_editor->snap_musical()) {
3708 pf = adjusted_current_frame (event, false);
3710 pf = adjusted_current_frame (event);
3713 /* adjust this and the next tempi to match pointer frame */
3714 double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3715 _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.frame_at_quarter_note (_grab_qn), pf);
3718 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3719 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3720 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3721 show_verbose_cursor_text (sstr.str());
3725 TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
3727 TempoMap& map (_editor->session()->tempo_map());
3729 if (!movement_occurred || !_drag_valid) {
3733 _editor->tempo_curve_selected (_tempo, false);
3734 _editor->tempo_curve_selected (_next_tempo, false);
3736 XMLNode &after = map.get_state();
3737 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3738 _editor->commit_reversible_command ();
3742 TempoTwistDrag::aborted (bool moved)
3745 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3749 TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
3755 DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
3760 TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3762 Drag::start_grab (event, cursor);
3763 TempoMap& map (_editor->session()->tempo_map());
3764 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3765 _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), true);
3768 sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3769 show_verbose_cursor_text (sstr.str());
3773 TempoEndDrag::setup_pointer_frame_offset ()
3775 TempoMap& map (_editor->session()->tempo_map());
3776 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3777 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3780 if (divisions > 0) {
3781 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3783 /* while it makes some sense for the user to determine the division to 'grab',
3784 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3785 and the result over steep tempo curves. Use sixteenths.
3787 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3790 _grab_qn = map.quarter_note_at_beat (beat);
3792 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3797 TempoEndDrag::motion (GdkEvent* event, bool first_move)
3799 TempoMap& map (_editor->session()->tempo_map());
3802 /* get current state */
3803 before_state = &map.get_state();
3804 _editor->begin_reversible_command (_("stretch end tempo"));
3809 framepos_t const pf = adjusted_current_frame (event, false);
3810 map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
3813 sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute();
3814 show_verbose_cursor_text (sstr.str());
3818 TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
3820 if (!movement_occurred) {
3824 TempoMap& map (_editor->session()->tempo_map());
3826 XMLNode &after = map.get_state();
3827 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3828 _editor->commit_reversible_command ();
3829 _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), false);
3833 TempoEndDrag::aborted (bool moved)
3836 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3840 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3841 : Drag (e, &c.track_canvas_item(), false)
3846 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3849 /** Do all the things we do when dragging the playhead to make it look as though
3850 * we have located, without actually doing the locate (because that would cause
3851 * the diskstream buffers to be refilled, which is too slow).
3854 CursorDrag::fake_locate (framepos_t t)
3856 if (_editor->session () == 0) {
3860 _editor->playhead_cursor->set_position (t);
3862 Session* s = _editor->session ();
3863 if (s->timecode_transmission_suspended ()) {
3864 framepos_t const f = _editor->playhead_cursor->current_frame ();
3865 /* This is asynchronous so it will be sent "now"
3867 s->send_mmc_locate (f);
3868 /* These are synchronous and will be sent during the next
3871 s->queue_full_time_code ();
3872 s->queue_song_position_pointer ();
3875 show_verbose_cursor_time (t);
3876 _editor->UpdateAllTransportClocks (t);
3880 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3882 Drag::start_grab (event, c);
3883 setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
3885 _grab_zoom = _editor->samples_per_pixel;
3887 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3889 _editor->snap_to_with_modifier (where, event);
3890 _editor->_dragging_playhead = true;
3891 _editor->_control_scroll_target = where.frame;
3893 Session* s = _editor->session ();
3895 /* grab the track canvas item as well */
3897 _cursor.track_canvas_item().grab();
3900 if (_was_rolling && _stop) {
3904 if (s->is_auditioning()) {
3905 s->cancel_audition ();
3909 if (AudioEngine::instance()->connected()) {
3911 /* do this only if we're the engine is connected
3912 * because otherwise this request will never be
3913 * serviced and we'll busy wait forever. likewise,
3914 * notice if we are disconnected while waiting for the
3915 * request to be serviced.
3918 s->request_suspend_timecode_transmission ();
3919 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3920 /* twiddle our thumbs */
3925 fake_locate (where.frame - snap_delta (event->button.state));
3929 CursorDrag::motion (GdkEvent* event, bool)
3931 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3933 _editor->snap_to_with_modifier (where, event);
3935 if (where.frame != last_pointer_frame()) {
3936 fake_locate (where.frame - snap_delta (event->button.state));
3941 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3943 _editor->_dragging_playhead = false;
3945 _cursor.track_canvas_item().ungrab();
3947 if (!movement_occurred && _stop) {
3951 motion (event, false);
3953 Session* s = _editor->session ();
3955 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3956 _editor->_pending_locate_request = true;
3957 s->request_resume_timecode_transmission ();
3962 CursorDrag::aborted (bool)
3964 _cursor.track_canvas_item().ungrab();
3966 if (_editor->_dragging_playhead) {
3967 _editor->session()->request_resume_timecode_transmission ();
3968 _editor->_dragging_playhead = false;
3971 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false).frame);
3974 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3975 : RegionDrag (e, i, p, v)
3977 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3981 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3983 Drag::start_grab (event, cursor);
3985 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3986 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3987 setup_snap_delta (MusicFrame (r->position(), 0));
3989 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3993 FadeInDrag::setup_pointer_frame_offset ()
3995 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3996 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3997 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
4001 FadeInDrag::motion (GdkEvent* event, bool)
4003 framecnt_t fade_length;
4005 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4006 _editor->snap_to_with_modifier (pos, event);
4008 pos.frame -= snap_delta (event->button.state);
4010 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4012 if (pos.frame < (region->position() + 64)) {
4013 fade_length = 64; // this should be a minimum defined somewhere
4014 } else if (pos.frame > region->position() + region->length() - region->fade_out()->back()->when) {
4015 fade_length = region->length() - region->fade_out()->back()->when - 1;
4017 fade_length = pos.frame - region->position();
4020 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4022 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4028 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
4031 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
4035 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
4037 if (!movement_occurred) {
4041 framecnt_t fade_length;
4042 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4044 _editor->snap_to_with_modifier (pos, event);
4045 pos.frame -= snap_delta (event->button.state);
4047 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4049 if (pos.frame < (region->position() + 64)) {
4050 fade_length = 64; // this should be a minimum defined somewhere
4051 } else if (pos.frame >= region->position() + region->length() - region->fade_out()->back()->when) {
4052 fade_length = region->length() - region->fade_out()->back()->when - 1;
4054 fade_length = pos.frame - region->position();
4057 bool in_command = false;
4059 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4061 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4067 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4068 XMLNode &before = alist->get_state();
4070 tmp->audio_region()->set_fade_in_length (fade_length);
4071 tmp->audio_region()->set_fade_in_active (true);
4074 _editor->begin_reversible_command (_("change fade in length"));
4077 XMLNode &after = alist->get_state();
4078 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4082 _editor->commit_reversible_command ();
4087 FadeInDrag::aborted (bool)
4089 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4090 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4096 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
4100 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4101 : RegionDrag (e, i, p, v)
4103 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
4107 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4109 Drag::start_grab (event, cursor);
4111 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4112 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4113 setup_snap_delta (MusicFrame (r->last_frame(), 0));
4115 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
4119 FadeOutDrag::setup_pointer_frame_offset ()
4121 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4122 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4123 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
4127 FadeOutDrag::motion (GdkEvent* event, bool)
4129 framecnt_t fade_length;
4130 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4132 _editor->snap_to_with_modifier (pos, event);
4133 pos.frame -= snap_delta (event->button.state);
4135 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4137 if (pos.frame > (region->last_frame() - 64)) {
4138 fade_length = 64; // this should really be a minimum fade defined somewhere
4139 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4140 fade_length = region->length() - region->fade_in()->back()->when - 1;
4142 fade_length = region->last_frame() - pos.frame;
4145 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4147 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4153 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
4156 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
4160 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
4162 if (!movement_occurred) {
4166 framecnt_t fade_length;
4167 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4169 _editor->snap_to_with_modifier (pos, event);
4170 pos.frame -= snap_delta (event->button.state);
4172 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4174 if (pos.frame > (region->last_frame() - 64)) {
4175 fade_length = 64; // this should really be a minimum fade defined somewhere
4176 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4177 fade_length = region->length() - region->fade_in()->back()->when - 1;
4179 fade_length = region->last_frame() - pos.frame;
4182 bool in_command = false;
4184 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4186 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4192 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
4193 XMLNode &before = alist->get_state();
4195 tmp->audio_region()->set_fade_out_length (fade_length);
4196 tmp->audio_region()->set_fade_out_active (true);
4199 _editor->begin_reversible_command (_("change fade out length"));
4202 XMLNode &after = alist->get_state();
4203 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4207 _editor->commit_reversible_command ();
4212 FadeOutDrag::aborted (bool)
4214 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4215 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4221 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
4225 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4227 , _selection_changed (false)
4229 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4230 Gtk::Window* toplevel = _editor->current_toplevel();
4231 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4235 _points.push_back (ArdourCanvas::Duple (0, 0));
4237 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4240 MarkerDrag::~MarkerDrag ()
4242 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4247 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4249 location = new Location (*l);
4250 markers.push_back (m);
4255 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4257 Drag::start_grab (event, cursor);
4261 Location *location = _editor->find_location_from_marker (_marker, is_start);
4262 _editor->_dragging_edit_point = true;
4264 update_item (location);
4266 // _drag_line->show();
4267 // _line->raise_to_top();
4270 show_verbose_cursor_time (location->start());
4272 show_verbose_cursor_time (location->end());
4274 setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
4276 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4279 case Selection::Toggle:
4280 /* we toggle on the button release */
4282 case Selection::Set:
4283 if (!_editor->selection->selected (_marker)) {
4284 _editor->selection->set (_marker);
4285 _selection_changed = true;
4288 case Selection::Extend:
4290 Locations::LocationList ll;
4291 list<ArdourMarker*> to_add;
4293 _editor->selection->markers.range (s, e);
4294 s = min (_marker->position(), s);
4295 e = max (_marker->position(), e);
4298 if (e < max_framepos) {
4301 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4302 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4303 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4306 to_add.push_back (lm->start);
4309 to_add.push_back (lm->end);
4313 if (!to_add.empty()) {
4314 _editor->selection->add (to_add);
4315 _selection_changed = true;
4319 case Selection::Add:
4320 _editor->selection->add (_marker);
4321 _selection_changed = true;
4326 /* Set up copies for us to manipulate during the drag
4329 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4331 Location* l = _editor->find_location_from_marker (*i, is_start);
4338 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4340 /* range: check that the other end of the range isn't
4343 CopiedLocationInfo::iterator x;
4344 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4345 if (*(*x).location == *l) {
4349 if (x == _copied_locations.end()) {
4350 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4352 (*x).markers.push_back (*i);
4353 (*x).move_both = true;
4361 MarkerDrag::setup_pointer_frame_offset ()
4364 Location *location = _editor->find_location_from_marker (_marker, is_start);
4365 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4369 MarkerDrag::motion (GdkEvent* event, bool)
4371 framecnt_t f_delta = 0;
4373 bool move_both = false;
4374 Location *real_location;
4375 Location *copy_location = 0;
4376 framecnt_t const sd = snap_delta (event->button.state);
4378 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true).frame - sd;
4379 framepos_t next = newframe;
4381 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4385 CopiedLocationInfo::iterator x;
4387 /* find the marker we're dragging, and compute the delta */
4389 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4391 copy_location = (*x).location;
4393 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4395 /* this marker is represented by this
4396 * CopiedLocationMarkerInfo
4399 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4404 if (real_location->is_mark()) {
4405 f_delta = newframe - copy_location->start();
4409 switch (_marker->type()) {
4410 case ArdourMarker::SessionStart:
4411 case ArdourMarker::RangeStart:
4412 case ArdourMarker::LoopStart:
4413 case ArdourMarker::PunchIn:
4414 f_delta = newframe - copy_location->start();
4417 case ArdourMarker::SessionEnd:
4418 case ArdourMarker::RangeEnd:
4419 case ArdourMarker::LoopEnd:
4420 case ArdourMarker::PunchOut:
4421 f_delta = newframe - copy_location->end();
4424 /* what kind of marker is this ? */
4433 if (x == _copied_locations.end()) {
4434 /* hmm, impossible - we didn't find the dragged marker */
4438 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4440 /* now move them all */
4442 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4444 copy_location = x->location;
4446 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4450 if (real_location->locked()) {
4454 if (copy_location->is_mark()) {
4457 copy_location->set_start (copy_location->start() + f_delta, false, true, divisions);
4461 framepos_t new_start = copy_location->start() + f_delta;
4462 framepos_t new_end = copy_location->end() + f_delta;
4464 if (is_start) { // start-of-range marker
4466 if (move_both || (*x).move_both) {
4467 copy_location->set_start (new_start, false, true, divisions);
4468 copy_location->set_end (new_end, false, true, divisions);
4469 } else if (new_start < copy_location->end()) {
4470 copy_location->set_start (new_start, false, true, divisions);
4471 } else if (newframe > 0) {
4472 //_editor->snap_to (next, RoundUpAlways, true);
4473 copy_location->set_end (next, false, true, divisions);
4474 copy_location->set_start (newframe, false, true, divisions);
4477 } else { // end marker
4479 if (move_both || (*x).move_both) {
4480 copy_location->set_end (new_end, divisions);
4481 copy_location->set_start (new_start, false, true, divisions);
4482 } else if (new_end > copy_location->start()) {
4483 copy_location->set_end (new_end, false, true, divisions);
4484 } else if (newframe > 0) {
4485 //_editor->snap_to (next, RoundDownAlways, true);
4486 copy_location->set_start (next, false, true, divisions);
4487 copy_location->set_end (newframe, false, true, divisions);
4492 update_item (copy_location);
4494 /* now lookup the actual GUI items used to display this
4495 * location and move them to wherever the copy of the location
4496 * is now. This means that the logic in ARDOUR::Location is
4497 * still enforced, even though we are not (yet) modifying
4498 * the real Location itself.
4501 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4504 lm->set_position (copy_location->start(), copy_location->end());
4509 assert (!_copied_locations.empty());
4511 show_verbose_cursor_time (newframe);
4515 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4517 if (!movement_occurred) {
4519 if (was_double_click()) {
4520 _editor->rename_marker (_marker);
4524 /* just a click, do nothing but finish
4525 off the selection process
4528 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4530 case Selection::Set:
4531 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4532 _editor->selection->set (_marker);
4533 _selection_changed = true;
4537 case Selection::Toggle:
4538 /* we toggle on the button release, click only */
4539 _editor->selection->toggle (_marker);
4540 _selection_changed = true;
4544 case Selection::Extend:
4545 case Selection::Add:
4549 if (_selection_changed) {
4550 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4551 _editor->commit_reversible_selection_op();
4557 _editor->_dragging_edit_point = false;
4559 XMLNode &before = _editor->session()->locations()->get_state();
4560 bool in_command = false;
4562 MarkerSelection::iterator i;
4563 CopiedLocationInfo::iterator x;
4564 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4567 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4568 x != _copied_locations.end() && i != _editor->selection->markers.end();
4571 Location * location = _editor->find_location_from_marker (*i, is_start);
4575 if (location->locked()) {
4579 _editor->begin_reversible_command ( _("move marker") );
4582 if (location->is_mark()) {
4583 location->set_start (((*x).location)->start(), false, true, divisions);
4585 location->set (((*x).location)->start(), ((*x).location)->end(), true, divisions);
4588 if (location->is_session_range()) {
4589 _editor->session()->set_end_is_free (false);
4595 XMLNode &after = _editor->session()->locations()->get_state();
4596 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4597 _editor->commit_reversible_command ();
4602 MarkerDrag::aborted (bool movement_occurred)
4604 if (!movement_occurred) {
4608 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4610 /* move all markers to their original location */
4613 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4616 Location * location = _editor->find_location_from_marker (*m, is_start);
4619 (*m)->set_position (is_start ? location->start() : location->end());
4626 MarkerDrag::update_item (Location*)
4631 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4633 , _fixed_grab_x (0.0)
4634 , _fixed_grab_y (0.0)
4635 , _cumulative_x_drag (0.0)
4636 , _cumulative_y_drag (0.0)
4640 if (_zero_gain_fraction < 0.0) {
4641 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4644 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4646 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4652 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4654 Drag::start_grab (event, _editor->cursors()->fader);
4656 // start the grab at the center of the control point so
4657 // the point doesn't 'jump' to the mouse after the first drag
4658 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4659 _fixed_grab_y = _point->get_y();
4661 setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
4663 float const fraction = 1 - (_point->get_y() / _point->line().height());
4664 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4666 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4668 if (!_point->can_slide ()) {
4669 _x_constrained = true;
4674 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4676 double dx = _drags->current_pointer_x() - last_pointer_x();
4677 double dy = current_pointer_y() - last_pointer_y();
4678 bool need_snap = true;
4680 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4686 /* coordinate in pixels relative to the start of the region (for region-based automation)
4687 or track (for track-based automation) */
4688 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4689 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4691 // calculate zero crossing point. back off by .01 to stay on the
4692 // positive side of zero
4693 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4695 if (_x_constrained) {
4698 if (_y_constrained) {
4702 _cumulative_x_drag = cx - _fixed_grab_x;
4703 _cumulative_y_drag = cy - _fixed_grab_y;
4707 cy = min ((double) _point->line().height(), cy);
4709 // make sure we hit zero when passing through
4710 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4714 MusicFrame cx_mf (_editor->pixel_to_sample (cx) + snap_delta (event->button.state), 0);
4716 if (!_x_constrained && need_snap) {
4717 _editor->snap_to_with_modifier (cx_mf, event);
4720 cx_mf.frame -= snap_delta (event->button.state);
4721 cx_mf.frame = min (cx_mf.frame, _point->line().maximum_time() + _point->line().offset());
4723 float const fraction = 1.0 - (cy / _point->line().height());
4726 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4727 _editor->begin_reversible_command (_("automation event move"));
4728 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4730 pair<double, float> result;
4731 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
4733 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4737 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4739 if (!movement_occurred) {
4742 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4743 _editor->reset_point_selection ();
4747 _point->line().end_drag (_pushing, _final_index);
4748 _editor->commit_reversible_command ();
4753 ControlPointDrag::aborted (bool)
4755 _point->line().reset ();
4759 ControlPointDrag::active (Editing::MouseMode m)
4761 if (m == Editing::MouseDraw) {
4762 /* always active in mouse draw */
4766 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4767 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4770 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4773 , _fixed_grab_x (0.0)
4774 , _fixed_grab_y (0.0)
4775 , _cumulative_y_drag (0)
4779 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4783 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4785 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4788 _item = &_line->grab_item ();
4790 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4791 origin, and ditto for y.
4794 double mx = event->button.x;
4795 double my = event->button.y;
4797 _line->grab_item().canvas_to_item (mx, my);
4799 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4801 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4802 /* no adjacent points */
4806 Drag::start_grab (event, _editor->cursors()->fader);
4808 /* store grab start in item frame */
4809 double const bx = _line->nth (_before)->get_x();
4810 double const ax = _line->nth (_after)->get_x();
4811 double const click_ratio = (ax - mx) / (ax - bx);
4813 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4818 double fraction = 1.0 - (cy / _line->height());
4820 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4824 LineDrag::motion (GdkEvent* event, bool first_move)
4826 double dy = current_pointer_y() - last_pointer_y();
4828 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4832 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4834 _cumulative_y_drag = cy - _fixed_grab_y;
4837 cy = min ((double) _line->height(), cy);
4839 double const fraction = 1.0 - (cy / _line->height());
4843 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4845 _editor->begin_reversible_command (_("automation range move"));
4846 _line->start_drag_line (_before, _after, initial_fraction);
4849 /* we are ignoring x position for this drag, so we can just pass in anything */
4850 pair<double, float> result;
4852 result = _line->drag_motion (0, fraction, true, false, ignored);
4853 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4857 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4859 if (movement_occurred) {
4860 motion (event, false);
4861 _line->end_drag (false, 0);
4862 _editor->commit_reversible_command ();
4864 /* add a new control point on the line */
4866 AutomationTimeAxisView* atv;
4868 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4869 framepos_t where = grab_frame ();
4872 double cy = _fixed_grab_y;
4874 _line->grab_item().item_to_canvas (cx, cy);
4876 atv->add_automation_event (event, where, cy, false);
4877 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4878 AudioRegionView* arv;
4880 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4881 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4888 LineDrag::aborted (bool)
4893 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4897 _region_view_grab_x (0.0),
4898 _cumulative_x_drag (0),
4902 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4906 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4908 Drag::start_grab (event);
4910 _line = reinterpret_cast<Line*> (_item);
4913 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4915 double cx = event->button.x;
4916 double cy = event->button.y;
4918 _item->parent()->canvas_to_item (cx, cy);
4920 /* store grab start in parent frame */
4921 _region_view_grab_x = cx;
4923 _before = *(float*) _item->get_data ("position");
4925 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4927 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4931 FeatureLineDrag::motion (GdkEvent*, bool)
4933 double dx = _drags->current_pointer_x() - last_pointer_x();
4935 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4937 _cumulative_x_drag += dx;
4939 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4948 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4950 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4952 float *pos = new float;
4955 _line->set_data ("position", pos);
4961 FeatureLineDrag::finished (GdkEvent*, bool)
4963 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4964 _arv->update_transient(_before, _before);
4968 FeatureLineDrag::aborted (bool)
4973 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4975 , _vertical_only (false)
4977 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4981 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4983 Drag::start_grab (event);
4984 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4988 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4994 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4995 MusicFrame grab (grab_frame (), 0);
4997 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4998 _editor->snap_to_with_modifier (grab, event);
5000 grab.frame = raw_grab_frame ();
5003 /* base start and end on initial click position */
5005 if (pf < grab.frame) {
5013 if (current_pointer_y() < grab_y()) {
5014 y1 = current_pointer_y();
5017 y2 = current_pointer_y();
5021 if (start != end || y1 != y2) {
5023 double x1 = _editor->sample_to_pixel (start);
5024 double x2 = _editor->sample_to_pixel (end);
5025 const double min_dimension = 2.0;
5027 if (_vertical_only) {
5028 /* fixed 10 pixel width */
5032 x2 = min (x1 - min_dimension, x2);
5034 x2 = max (x1 + min_dimension, x2);
5039 y2 = min (y1 - min_dimension, y2);
5041 y2 = max (y1 + min_dimension, y2);
5044 /* translate rect into item space and set */
5046 ArdourCanvas::Rect r (x1, y1, x2, y2);
5048 /* this drag is a _trackview_only == true drag, so the y1 and
5049 * y2 (computed using current_pointer_y() and grab_y()) will be
5050 * relative to the top of the trackview group). The
5051 * rubberband rect has the same parent/scroll offset as the
5052 * the trackview group, so we can use the "r" rect directly
5053 * to set the shape of the rubberband.
5056 _editor->rubberband_rect->set (r);
5057 _editor->rubberband_rect->show();
5058 _editor->rubberband_rect->raise_to_top();
5060 show_verbose_cursor_time (pf);
5062 do_select_things (event, true);
5067 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
5071 framepos_t grab = grab_frame ();
5072 framepos_t lpf = last_pointer_frame ();
5074 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5075 grab = raw_grab_frame ();
5076 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
5090 if (current_pointer_y() < grab_y()) {
5091 y1 = current_pointer_y();
5094 y2 = current_pointer_y();
5098 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
5102 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
5104 if (movement_occurred) {
5106 motion (event, false);
5107 do_select_things (event, false);
5113 bool do_deselect = true;
5114 MidiTimeAxisView* mtv;
5116 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
5118 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
5119 /* nothing selected */
5120 add_midi_region (mtv, true);
5121 do_deselect = false;
5125 /* do not deselect if Primary or Tertiary (toggle-select or
5126 * extend-select are pressed.
5129 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
5130 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
5137 _editor->rubberband_rect->hide();
5141 RubberbandSelectDrag::aborted (bool)
5143 _editor->rubberband_rect->hide ();
5146 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
5147 : RegionDrag (e, i, p, v)
5149 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
5153 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5155 Drag::start_grab (event, cursor);
5157 _editor->get_selection().add (_primary);
5159 MusicFrame where (_primary->region()->position(), 0);
5160 setup_snap_delta (where);
5162 show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
5166 TimeFXDrag::motion (GdkEvent* event, bool)
5168 RegionView* rv = _primary;
5169 StreamView* cv = rv->get_time_axis_view().view ();
5170 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
5171 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
5172 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
5173 MusicFrame pf (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
5175 _editor->snap_to_with_modifier (pf, event);
5176 pf.frame -= snap_delta (event->button.state);
5178 if (pf.frame > rv->region()->position()) {
5179 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf.frame, layers, layer);
5182 show_verbose_cursor_duration (_primary->region()->position(), pf.frame, 0);
5186 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
5188 /* this may have been a single click, no drag. We still want the dialog
5189 to show up in that case, so that the user can manually edit the
5190 parameters for the timestretch.
5193 float fraction = 1.0;
5195 if (movement_occurred) {
5197 motion (event, false);
5199 _primary->get_time_axis_view().hide_timestretch ();
5201 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
5203 if (adjusted_frame_pos < _primary->region()->position()) {
5204 /* backwards drag of the left edge - not usable */
5208 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
5210 fraction = (double) newlen / (double) _primary->region()->length();
5212 #ifndef USE_RUBBERBAND
5213 // Soundtouch uses fraction / 100 instead of normal (/ 1)
5214 if (_primary->region()->data_type() == DataType::AUDIO) {
5215 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
5220 if (!_editor->get_selection().regions.empty()) {
5221 /* primary will already be included in the selection, and edit
5222 group shared editing will propagate selection across
5223 equivalent regions, so just use the current region
5227 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5228 error << _("An error occurred while executing time stretch operation") << endmsg;
5234 TimeFXDrag::aborted (bool)
5236 _primary->get_time_axis_view().hide_timestretch ();
5239 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5242 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5246 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5248 Drag::start_grab (event);
5252 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5254 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5258 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5260 if (movement_occurred && _editor->session()) {
5261 /* make sure we stop */
5262 _editor->session()->request_transport_speed (0.0);
5267 ScrubDrag::aborted (bool)
5272 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5276 , _track_selection_at_start (e)
5277 , _time_selection_at_start (!_editor->get_selection().time.empty())
5279 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5281 if (_time_selection_at_start) {
5282 start_at_start = _editor->get_selection().time.start();
5283 end_at_start = _editor->get_selection().time.end_frame();
5288 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5290 if (_editor->session() == 0) {
5294 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5296 switch (_operation) {
5297 case CreateSelection:
5298 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5303 cursor = _editor->cursors()->selector;
5304 Drag::start_grab (event, cursor);
5307 case SelectionStartTrim:
5308 if (_editor->clicked_axisview) {
5309 _editor->clicked_axisview->order_selection_trims (_item, true);
5311 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5314 case SelectionEndTrim:
5315 if (_editor->clicked_axisview) {
5316 _editor->clicked_axisview->order_selection_trims (_item, false);
5318 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5322 Drag::start_grab (event, cursor);
5325 case SelectionExtend:
5326 Drag::start_grab (event, cursor);
5330 if (_operation == SelectionMove) {
5331 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5333 show_verbose_cursor_time (adjusted_current_frame (event));
5338 SelectionDrag::setup_pointer_frame_offset ()
5340 switch (_operation) {
5341 case CreateSelection:
5342 _pointer_frame_offset = 0;
5345 case SelectionStartTrim:
5347 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5350 case SelectionEndTrim:
5351 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5354 case SelectionExtend:
5360 SelectionDrag::motion (GdkEvent* event, bool first_move)
5362 framepos_t start = 0;
5364 framecnt_t length = 0;
5365 framecnt_t distance = 0;
5366 MusicFrame start_mf (0, 0);
5367 framepos_t const pending_position = adjusted_current_frame (event);
5369 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5374 _track_selection_at_start = _editor->selection->tracks;
5377 switch (_operation) {
5378 case CreateSelection:
5380 MusicFrame grab (grab_frame (), 0);
5382 grab.frame = adjusted_current_frame (event, false);
5383 if (grab.frame < pending_position) {
5384 _editor->snap_to (grab, RoundDownMaybe);
5386 _editor->snap_to (grab, RoundUpMaybe);
5390 if (pending_position < grab.frame) {
5391 start = pending_position;
5394 end = pending_position;
5398 /* first drag: Either add to the selection
5399 or create a new selection
5406 /* adding to the selection */
5407 _editor->set_selected_track_as_side_effect (Selection::Add);
5408 _editor->clicked_selection = _editor->selection->add (start, end);
5415 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5416 _editor->set_selected_track_as_side_effect (Selection::Set);
5419 _editor->clicked_selection = _editor->selection->set (start, end);
5423 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5424 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5425 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5427 _editor->selection->add (atest);
5431 /* select all tracks within the rectangle that we've marked out so far */
5432 TrackViewList new_selection;
5433 TrackViewList& all_tracks (_editor->track_views);
5435 ArdourCanvas::Coord const top = grab_y();
5436 ArdourCanvas::Coord const bottom = current_pointer_y();
5438 if (top >= 0 && bottom >= 0) {
5440 //first, find the tracks that are covered in the y range selection
5441 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5442 if ((*i)->covered_by_y_range (top, bottom)) {
5443 new_selection.push_back (*i);
5447 //now compare our list with the current selection, and add as necessary
5448 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5449 TrackViewList tracks_to_add;
5450 TrackViewList tracks_to_remove;
5451 vector<RouteGroup*> selected_route_groups;
5454 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
5455 if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
5456 tracks_to_remove.push_back (*i);
5458 RouteGroup* rg = (*i)->route_group();
5459 if (rg && rg->is_active() && rg->is_select()) {
5460 selected_route_groups.push_back (rg);
5466 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5467 if (!_editor->selection->tracks.contains (*i)) {
5468 tracks_to_add.push_back (*i);
5469 RouteGroup* rg = (*i)->route_group();
5471 if (rg && rg->is_active() && rg->is_select()) {
5472 selected_route_groups.push_back (rg);
5477 _editor->selection->add (tracks_to_add);
5479 if (!tracks_to_remove.empty()) {
5481 /* check all these to-be-removed tracks against the
5482 * possibility that they are selected by being
5483 * in the same group as an approved track.
5486 for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
5487 RouteGroup* rg = (*i)->route_group();
5489 if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
5490 i = tracks_to_remove.erase (i);
5496 /* remove whatever is left */
5498 _editor->selection->remove (tracks_to_remove);
5504 case SelectionStartTrim:
5506 end = _editor->selection->time[_editor->clicked_selection].end;
5508 if (pending_position > end) {
5511 start = pending_position;
5515 case SelectionEndTrim:
5517 start = _editor->selection->time[_editor->clicked_selection].start;
5519 if (pending_position < start) {
5522 end = pending_position;
5529 start = _editor->selection->time[_editor->clicked_selection].start;
5530 end = _editor->selection->time[_editor->clicked_selection].end;
5532 length = end - start;
5533 distance = pending_position - start;
5534 start = pending_position;
5536 start_mf.frame = start;
5537 _editor->snap_to (start_mf);
5539 end = start_mf.frame + length;
5543 case SelectionExtend:
5548 switch (_operation) {
5550 if (_time_selection_at_start) {
5551 _editor->selection->move_time (distance);
5555 _editor->selection->replace (_editor->clicked_selection, start, end);
5559 if (_operation == SelectionMove) {
5560 show_verbose_cursor_time(start);
5562 show_verbose_cursor_time(pending_position);
5567 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5569 Session* s = _editor->session();
5571 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5572 if (movement_occurred) {
5573 motion (event, false);
5574 /* XXX this is not object-oriented programming at all. ick */
5575 if (_editor->selection->time.consolidate()) {
5576 _editor->selection->TimeChanged ();
5579 /* XXX what if its a music time selection? */
5581 if (s->get_play_range() && s->transport_rolling()) {
5582 s->request_play_range (&_editor->selection->time, true);
5583 } else if (!s->config.get_external_sync()) {
5584 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5585 s->request_locate (_editor->get_selection().time.start());
5589 if (_editor->get_selection().time.length() != 0) {
5590 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5592 s->clear_range_selection ();
5597 /* just a click, no pointer movement.
5600 if (was_double_click()) {
5601 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5602 _editor->temporal_zoom_selection (Both);
5607 if (_operation == SelectionExtend) {
5608 if (_time_selection_at_start) {
5609 framepos_t pos = adjusted_current_frame (event, false);
5610 framepos_t start = min (pos, start_at_start);
5611 framepos_t end = max (pos, end_at_start);
5612 _editor->selection->set (start, end);
5615 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5616 if (_editor->clicked_selection) {
5617 _editor->selection->remove (_editor->clicked_selection);
5620 if (!_editor->clicked_selection) {
5621 _editor->selection->clear_time();
5626 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5627 _editor->selection->set (_editor->clicked_axisview);
5630 if (s && s->get_play_range () && s->transport_rolling()) {
5631 s->request_stop (false, false);
5636 _editor->stop_canvas_autoscroll ();
5637 _editor->clicked_selection = 0;
5638 _editor->commit_reversible_selection_op ();
5642 SelectionDrag::aborted (bool)
5647 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5648 : Drag (e, i, false),
5652 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5654 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5655 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5656 physical_screen_height (_editor->current_toplevel()->get_window())));
5657 _drag_rect->hide ();
5659 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5660 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5663 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5665 /* normal canvas items will be cleaned up when their parent group is deleted. But
5666 this item is created as the child of a long-lived parent group, and so we
5667 need to explicitly delete it.
5673 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5675 if (_editor->session() == 0) {
5679 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5681 if (!_editor->temp_location) {
5682 _editor->temp_location = new Location (*_editor->session());
5685 switch (_operation) {
5686 case CreateSkipMarker:
5687 case CreateRangeMarker:
5688 case CreateTransportMarker:
5689 case CreateCDMarker:
5691 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5696 cursor = _editor->cursors()->selector;
5700 Drag::start_grab (event, cursor);
5702 show_verbose_cursor_time (adjusted_current_frame (event));
5706 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5708 framepos_t start = 0;
5710 ArdourCanvas::Rectangle *crect;
5712 switch (_operation) {
5713 case CreateSkipMarker:
5714 crect = _editor->range_bar_drag_rect;
5716 case CreateRangeMarker:
5717 crect = _editor->range_bar_drag_rect;
5719 case CreateTransportMarker:
5720 crect = _editor->transport_bar_drag_rect;
5722 case CreateCDMarker:
5723 crect = _editor->cd_marker_bar_drag_rect;
5726 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5731 framepos_t const pf = adjusted_current_frame (event);
5733 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5734 MusicFrame grab (grab_frame (), 0);
5735 _editor->snap_to (grab);
5737 if (pf < grab_frame()) {
5745 /* first drag: Either add to the selection
5746 or create a new selection.
5751 _editor->temp_location->set (start, end);
5755 update_item (_editor->temp_location);
5757 //_drag_rect->raise_to_top();
5763 _editor->temp_location->set (start, end);
5765 double x1 = _editor->sample_to_pixel (start);
5766 double x2 = _editor->sample_to_pixel (end);
5770 update_item (_editor->temp_location);
5773 show_verbose_cursor_time (pf);
5778 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5780 Location * newloc = 0;
5784 if (movement_occurred) {
5785 motion (event, false);
5788 switch (_operation) {
5789 case CreateSkipMarker:
5790 case CreateRangeMarker:
5791 case CreateCDMarker:
5793 XMLNode &before = _editor->session()->locations()->get_state();
5794 if (_operation == CreateSkipMarker) {
5795 _editor->begin_reversible_command (_("new skip marker"));
5796 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5797 flags = Location::IsRangeMarker | Location::IsSkip;
5798 _editor->range_bar_drag_rect->hide();
5799 } else if (_operation == CreateCDMarker) {
5800 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5801 _editor->begin_reversible_command (_("new CD marker"));
5802 flags = Location::IsRangeMarker | Location::IsCDMarker;
5803 _editor->cd_marker_bar_drag_rect->hide();
5805 _editor->begin_reversible_command (_("new skip marker"));
5806 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5807 flags = Location::IsRangeMarker;
5808 _editor->range_bar_drag_rect->hide();
5810 newloc = new Location (
5811 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5812 , _editor->get_grid_music_divisions (event->button.state));
5814 _editor->session()->locations()->add (newloc, true);
5815 XMLNode &after = _editor->session()->locations()->get_state();
5816 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5817 _editor->commit_reversible_command ();
5821 case CreateTransportMarker:
5822 // popup menu to pick loop or punch
5823 _editor->new_transport_marker_context_menu (&event->button, _item);
5829 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5831 if (_operation == CreateTransportMarker) {
5833 /* didn't drag, so just locate */
5835 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5837 } else if (_operation == CreateCDMarker) {
5839 /* didn't drag, but mark is already created so do
5842 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5847 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5849 if (end == max_framepos) {
5850 end = _editor->session()->current_end_frame ();
5853 if (start == max_framepos) {
5854 start = _editor->session()->current_start_frame ();
5857 switch (_editor->mouse_mode) {
5859 /* find the two markers on either side and then make the selection from it */
5860 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5864 /* find the two markers on either side of the click and make the range out of it */
5865 _editor->selection->set (start, end);
5874 _editor->stop_canvas_autoscroll ();
5878 RangeMarkerBarDrag::aborted (bool movement_occurred)
5880 if (movement_occurred) {
5881 _drag_rect->hide ();
5886 RangeMarkerBarDrag::update_item (Location* location)
5888 double const x1 = _editor->sample_to_pixel (location->start());
5889 double const x2 = _editor->sample_to_pixel (location->end());
5891 _drag_rect->set_x0 (x1);
5892 _drag_rect->set_x1 (x2);
5895 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5897 , _cumulative_dx (0)
5898 , _cumulative_dy (0)
5900 , _was_selected (false)
5903 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5905 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5907 _region = &_primary->region_view ();
5908 _note_height = _region->midi_stream_view()->note_height ();
5912 NoteDrag::setup_pointer_frame_offset ()
5914 _pointer_frame_offset = raw_grab_frame()
5915 - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
5919 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5921 Drag::start_grab (event);
5923 if (ArdourKeyboard::indicates_copy (event->button.state)) {
5929 setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
5931 if (!(_was_selected = _primary->selected())) {
5933 /* tertiary-click means extend selection - we'll do that on button release,
5934 so don't add it here, because otherwise we make it hard to figure
5935 out the "extend-to" range.
5938 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5941 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5944 _region->note_selected (_primary, true);
5946 _editor->get_selection().clear_points();
5947 _region->unique_select (_primary);
5953 /** @return Current total drag x change in quarter notes */
5955 NoteDrag::total_dx (GdkEvent * event) const
5957 if (_x_constrained) {
5961 TempoMap& map (_editor->session()->tempo_map());
5964 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5966 /* primary note time */
5967 frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
5969 /* primary note time in quarter notes */
5970 double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
5972 /* new time of the primary note in session frames */
5973 frameoffset_t st = n + dx + snap_delta (event->button.state);
5975 /* possibly snap and return corresponding delta in quarter notes */
5976 MusicFrame snap (st, 0);
5977 _editor->snap_to_with_modifier (snap, event);
5978 double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
5980 /* prevent the earliest note being dragged earlier than the region's start position */
5981 if (_earliest + ret < _region->midi_region()->start_beats()) {
5982 ret -= (_earliest + ret) - _region->midi_region()->start_beats();
5988 /** @return Current total drag y change in note number */
5990 NoteDrag::total_dy () const
5992 if (_y_constrained) {
5996 double const y = _region->midi_view()->y_position ();
5997 /* new current note */
5998 uint8_t n = _region->y_to_note (current_pointer_y () - y);
6000 MidiStreamView* msv = _region->midi_stream_view ();
6001 n = max (msv->lowest_note(), n);
6002 n = min (msv->highest_note(), n);
6003 /* and work out delta */
6004 return n - _region->y_to_note (grab_y() - y);
6008 NoteDrag::motion (GdkEvent * event, bool first_move)
6011 _earliest = _region->earliest_in_selection().to_double();
6013 /* make copies of all the selected notes */
6014 _primary = _region->copy_selection (_primary);
6018 /* Total change in x and y since the start of the drag */
6019 double const dx_qn = total_dx (event);
6020 int8_t const dy = total_dy ();
6022 /* Now work out what we have to do to the note canvas items to set this new drag delta */
6023 double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
6024 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
6027 _cumulative_dx = dx_qn;
6028 _cumulative_dy += tdy;
6030 int8_t note_delta = total_dy();
6034 _region->move_copies (dx_qn, tdy, note_delta);
6036 _region->move_selection (dx_qn, tdy, note_delta);
6039 /* the new note value may be the same as the old one, but we
6040 * don't know what that means because the selection may have
6041 * involved more than one note and we might be doing something
6042 * odd with them. so show the note value anyway, always.
6045 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
6047 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
6053 NoteDrag::finished (GdkEvent* ev, bool moved)
6056 /* no motion - select note */
6058 if (_editor->current_mouse_mode() == Editing::MouseContent ||
6059 _editor->current_mouse_mode() == Editing::MouseDraw) {
6061 bool changed = false;
6063 if (_was_selected) {
6064 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6066 _region->note_deselected (_primary);
6069 _editor->get_selection().clear_points();
6070 _region->unique_select (_primary);
6074 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
6075 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6077 if (!extend && !add && _region->selection_size() > 1) {
6078 _editor->get_selection().clear_points();
6079 _region->unique_select (_primary);
6081 } else if (extend) {
6082 _region->note_selected (_primary, true, true);
6085 /* it was added during button press */
6092 _editor->begin_reversible_selection_op(X_("Select Note Release"));
6093 _editor->commit_reversible_selection_op();
6097 _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
6102 NoteDrag::aborted (bool)
6107 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
6108 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
6109 : Drag (editor, atv->base_item ())
6111 , _y_origin (atv->y_position())
6112 , _nothing_to_drag (false)
6114 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6115 setup (atv->lines ());
6118 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
6119 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
6120 : Drag (editor, rv->get_canvas_group ())
6122 , _y_origin (rv->get_time_axis_view().y_position())
6123 , _nothing_to_drag (false)
6126 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6128 list<boost::shared_ptr<AutomationLine> > lines;
6130 AudioRegionView* audio_view;
6131 AutomationRegionView* automation_view;
6132 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
6133 lines.push_back (audio_view->get_gain_line ());
6134 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
6135 lines.push_back (automation_view->line ());
6138 error << _("Automation range drag created for invalid region type") << endmsg;
6144 /** @param lines AutomationLines to drag.
6145 * @param offset Offset from the session start to the points in the AutomationLines.
6148 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
6150 /* find the lines that overlap the ranges being dragged */
6151 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
6152 while (i != lines.end ()) {
6153 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
6156 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
6158 /* check this range against all the AudioRanges that we are using */
6159 list<AudioRange>::const_iterator k = _ranges.begin ();
6160 while (k != _ranges.end()) {
6161 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
6167 /* add it to our list if it overlaps at all */
6168 if (k != _ranges.end()) {
6173 _lines.push_back (n);
6179 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
6183 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
6185 return 1.0 - ((global_y - _y_origin) / line->height());
6189 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
6191 const double v = list->eval(x);
6192 return _integral ? rint(v) : v;
6196 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6198 Drag::start_grab (event, cursor);
6200 /* Get line states before we start changing things */
6201 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6202 i->state = &i->line->get_state ();
6203 i->original_fraction = y_fraction (i->line, current_pointer_y());
6206 if (_ranges.empty()) {
6208 /* No selected time ranges: drag all points */
6209 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6210 uint32_t const N = i->line->npoints ();
6211 for (uint32_t j = 0; j < N; ++j) {
6212 i->points.push_back (i->line->nth (j));
6218 if (_nothing_to_drag) {
6224 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
6226 if (_nothing_to_drag && !first_move) {
6231 _editor->begin_reversible_command (_("automation range move"));
6233 if (!_ranges.empty()) {
6235 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
6237 framecnt_t const half = (i->start + i->end) / 2;
6239 /* find the line that this audio range starts in */
6240 list<Line>::iterator j = _lines.begin();
6241 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
6245 if (j != _lines.end()) {
6246 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6248 /* j is the line that this audio range starts in; fade into it;
6249 64 samples length plucked out of thin air.
6252 framepos_t a = i->start + 64;
6257 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
6258 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
6260 XMLNode &before = the_list->get_state();
6261 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6262 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6264 if (add_p || add_q) {
6265 _editor->session()->add_command (
6266 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6270 /* same thing for the end */
6273 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6277 if (j != _lines.end()) {
6278 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6280 /* j is the line that this audio range starts in; fade out of it;
6281 64 samples length plucked out of thin air.
6284 framepos_t b = i->end - 64;
6289 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6290 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6292 XMLNode &before = the_list->get_state();
6293 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6294 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6296 if (add_p || add_q) {
6297 _editor->session()->add_command (
6298 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6303 _nothing_to_drag = true;
6305 /* Find all the points that should be dragged and put them in the relevant
6306 points lists in the Line structs.
6309 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6311 uint32_t const N = i->line->npoints ();
6312 for (uint32_t j = 0; j < N; ++j) {
6314 /* here's a control point on this line */
6315 ControlPoint* p = i->line->nth (j);
6316 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6318 /* see if it's inside a range */
6319 list<AudioRange>::const_iterator k = _ranges.begin ();
6320 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6324 if (k != _ranges.end()) {
6325 /* dragging this point */
6326 _nothing_to_drag = false;
6327 i->points.push_back (p);
6333 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6334 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6338 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6339 float const f = y_fraction (l->line, current_pointer_y());
6340 /* we are ignoring x position for this drag, so we can just pass in anything */
6341 pair<double, float> result;
6343 result = l->line->drag_motion (0, f, true, false, ignored);
6344 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6349 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6351 if (_nothing_to_drag || !motion_occurred) {
6355 motion (event, false);
6356 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6357 i->line->end_drag (false, 0);
6360 _editor->commit_reversible_command ();
6364 AutomationRangeDrag::aborted (bool)
6366 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6371 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6373 , initial_time_axis_view (itav)
6375 /* note that time_axis_view may be null if the regionview was created
6376 * as part of a copy operation.
6378 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6379 layer = v->region()->layer ();
6380 initial_y = v->get_canvas_group()->position().y;
6381 initial_playlist = v->region()->playlist ();
6382 initial_position = v->region()->position ();
6383 initial_end = v->region()->position () + v->region()->length ();
6386 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6387 : Drag (e, i->canvas_item ())
6390 , _cumulative_dx (0)
6392 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6393 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6398 PatchChangeDrag::motion (GdkEvent* ev, bool)
6400 framepos_t f = adjusted_current_frame (ev);
6401 boost::shared_ptr<Region> r = _region_view->region ();
6402 f = max (f, r->position ());
6403 f = min (f, r->last_frame ());
6405 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6406 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6407 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6408 _cumulative_dx = dxu;
6412 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6414 if (!movement_occurred) {
6415 if (was_double_click()) {
6416 _region_view->edit_patch_change (_patch_change);
6421 boost::shared_ptr<Region> r (_region_view->region ());
6422 framepos_t f = adjusted_current_frame (ev);
6423 f = max (f, r->position ());
6424 f = min (f, r->last_frame ());
6426 _region_view->move_patch_change (
6428 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6433 PatchChangeDrag::aborted (bool)
6435 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6439 PatchChangeDrag::setup_pointer_frame_offset ()
6441 boost::shared_ptr<Region> region = _region_view->region ();
6442 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6445 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6446 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6453 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6455 _region_view->update_drag_selection (
6457 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6461 MidiRubberbandSelectDrag::deselect_things ()
6466 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6467 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6470 _vertical_only = true;
6474 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6476 double const y = _region_view->midi_view()->y_position ();
6478 y1 = max (0.0, y1 - y);
6479 y2 = max (0.0, y2 - y);
6481 _region_view->update_vertical_drag_selection (
6484 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6489 MidiVerticalSelectDrag::deselect_things ()
6494 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6495 : RubberbandSelectDrag (e, i)
6501 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6503 if (drag_in_progress) {
6504 /* We just want to select things at the end of the drag, not during it */
6508 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6510 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6512 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6514 _editor->commit_reversible_selection_op ();
6518 EditorRubberbandSelectDrag::deselect_things ()
6520 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6522 _editor->selection->clear_tracks();
6523 _editor->selection->clear_regions();
6524 _editor->selection->clear_points ();
6525 _editor->selection->clear_lines ();
6526 _editor->selection->clear_midi_notes ();
6528 _editor->commit_reversible_selection_op();
6531 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6536 _note[0] = _note[1] = 0;
6539 NoteCreateDrag::~NoteCreateDrag ()
6545 NoteCreateDrag::grid_frames (framepos_t t) const
6548 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6549 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6551 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6552 - _region_view->region_beats_to_region_frames (t_beats);
6556 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6558 Drag::start_grab (event, cursor);
6560 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6561 TempoMap& map (_editor->session()->tempo_map());
6563 const framepos_t pf = _drags->current_pointer_frame ();
6564 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6566 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6568 double eqaf = map.exact_qn_at_frame (pf, divisions);
6570 if (divisions != 0) {
6572 const double qaf = map.quarter_note_at_frame (pf);
6574 /* Hack so that we always snap to the note that we are over, instead of snapping
6575 to the next one if we're more than halfway through the one we're over.
6578 const double rem = eqaf - qaf;
6580 eqaf -= grid_beats.to_double();
6584 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6585 /* minimum initial length is grid beats */
6586 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6588 double const x0 = _editor->sample_to_pixel (_note[0]);
6589 double const x1 = _editor->sample_to_pixel (_note[1]);
6590 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6592 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6593 _drag_rect->set_outline_all ();
6594 _drag_rect->set_outline_color (0xffffff99);
6595 _drag_rect->set_fill_color (0xffffff66);
6599 NoteCreateDrag::motion (GdkEvent* event, bool)
6601 TempoMap& map (_editor->session()->tempo_map());
6602 const framepos_t pf = _drags->current_pointer_frame ();
6603 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6604 double eqaf = map.exact_qn_at_frame (pf, divisions);
6606 if (divisions != 0) {
6608 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6610 const double qaf = map.quarter_note_at_frame (pf);
6611 /* Hack so that we always snap to the note that we are over, instead of snapping
6612 to the next one if we're more than halfway through the one we're over.
6615 const double rem = eqaf - qaf;
6617 eqaf -= grid_beats.to_double();
6620 eqaf += grid_beats.to_double();
6622 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6624 double const x0 = _editor->sample_to_pixel (_note[0]);
6625 double const x1 = _editor->sample_to_pixel (_note[1]);
6626 _drag_rect->set_x0 (std::min(x0, x1));
6627 _drag_rect->set_x1 (std::max(x0, x1));
6631 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6633 /* we create a note even if there was no movement */
6634 framepos_t const start = min (_note[0], _note[1]);
6635 framepos_t const start_sess_rel = start + _region_view->region()->position();
6636 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6637 framecnt_t const g = grid_frames (start);
6639 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6643 TempoMap& map (_editor->session()->tempo_map());
6644 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6645 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6647 _region_view->clear_editor_note_selection();
6648 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6652 NoteCreateDrag::y_to_region (double y) const
6655 _region_view->get_canvas_group()->canvas_to_item (x, y);
6660 NoteCreateDrag::aborted (bool)
6665 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6673 HitCreateDrag::~HitCreateDrag ()
6678 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6680 Drag::start_grab (event, cursor);
6682 TempoMap& map (_editor->session()->tempo_map());
6684 _y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6686 const framepos_t pf = _drags->current_pointer_frame ();
6687 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6689 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6691 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6693 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6697 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6698 Evoral::Beats length = _region_view->get_grid_beats (pf);
6700 _editor->begin_reversible_command (_("Create Hit"));
6701 _region_view->clear_editor_note_selection();
6702 _region_view->create_note_at (start, _y, length, event->button.state, false);
6708 HitCreateDrag::motion (GdkEvent* event, bool)
6710 TempoMap& map (_editor->session()->tempo_map());
6712 const framepos_t pf = _drags->current_pointer_frame ();
6713 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6715 if (divisions == 0) {
6719 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6720 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6722 if (_last_pos == start) {
6726 Evoral::Beats length = _region_view->get_grid_beats (pf);
6728 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6730 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6734 _region_view->create_note_at (start, _y, length, event->button.state, false);
6740 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6742 _editor->commit_reversible_command ();
6747 HitCreateDrag::y_to_region (double y) const
6750 _region_view->get_canvas_group()->canvas_to_item (x, y);
6755 HitCreateDrag::aborted (bool)
6760 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6765 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6769 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6771 Drag::start_grab (event, cursor);
6775 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6781 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6784 distance = _drags->current_pointer_x() - grab_x();
6785 len = ar->fade_in()->back()->when;
6787 distance = grab_x() - _drags->current_pointer_x();
6788 len = ar->fade_out()->back()->when;
6791 /* how long should it be ? */
6793 new_length = len + _editor->pixel_to_sample (distance);
6795 /* now check with the region that this is legal */
6797 new_length = ar->verify_xfade_bounds (new_length, start);
6800 arv->reset_fade_in_shape_width (ar, new_length);
6802 arv->reset_fade_out_shape_width (ar, new_length);
6807 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6813 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6816 distance = _drags->current_pointer_x() - grab_x();
6817 len = ar->fade_in()->back()->when;
6819 distance = grab_x() - _drags->current_pointer_x();
6820 len = ar->fade_out()->back()->when;
6823 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6825 _editor->begin_reversible_command ("xfade trim");
6826 ar->playlist()->clear_owned_changes ();
6829 ar->set_fade_in_length (new_length);
6831 ar->set_fade_out_length (new_length);
6834 /* Adjusting the xfade may affect other regions in the playlist, so we need
6835 to get undo Commands from the whole playlist rather than just the
6839 vector<Command*> cmds;
6840 ar->playlist()->rdiff (cmds);
6841 _editor->session()->add_commands (cmds);
6842 _editor->commit_reversible_command ();
6847 CrossfadeEdgeDrag::aborted (bool)
6850 // arv->redraw_start_xfade ();
6852 // arv->redraw_end_xfade ();
6856 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6857 : Drag (e, item, true)
6858 , line (new EditorCursor (*e))
6860 line->set_position (pos);
6862 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6865 RegionCutDrag::~RegionCutDrag ()
6871 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6873 Drag::start_grab (event, c);
6874 motion (event, false);
6878 RegionCutDrag::motion (GdkEvent* event, bool)
6880 MusicFrame pos (_drags->current_pointer_frame(), 0);
6881 _editor->snap_to_with_modifier (pos, event);
6883 line->set_position (pos.frame);
6887 RegionCutDrag::finished (GdkEvent* event, bool)
6889 _editor->get_track_canvas()->canvas()->re_enter();
6892 MusicFrame pos (_drags->current_pointer_frame(), 0);
6893 _editor->snap_to_with_modifier (pos, event);
6896 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos.frame);
6902 _editor->split_regions_at (pos, rs, false);
6906 RegionCutDrag::aborted (bool)
6910 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6911 : Drag (e, item, true)
6916 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6918 Drag::start_grab (event, c);
6920 framepos_t where = _editor->canvas_event_sample(event);
6922 _editor->_dragging_playhead = true;
6924 _editor->playhead_cursor->set_position (where);
6928 RulerZoomDrag::motion (GdkEvent* event, bool)
6930 framepos_t where = _editor->canvas_event_sample(event);
6932 _editor->playhead_cursor->set_position (where);
6934 const double movement_limit = 20.0;
6935 const double scale = 1.08;
6936 const double y_delta = last_pointer_y() - current_pointer_y();
6938 if (y_delta > 0 && y_delta < movement_limit) {
6939 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6940 } else if (y_delta < 0 && y_delta > -movement_limit) {
6941 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6946 RulerZoomDrag::finished (GdkEvent*, bool)
6948 _editor->_dragging_playhead = false;
6950 Session* s = _editor->session ();
6952 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6953 _editor->_pending_locate_request = true;
6959 RulerZoomDrag::aborted (bool)