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 , _constraint_pressed (false)
247 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
253 _cursor_ctx = CursorContext::create (*_editor, cursor);
255 _cursor_ctx->change (cursor);
262 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
265 /* we set up x/y dragging constraints on first move */
266 _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
268 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
270 setup_pointer_frame_offset ();
271 _grab_frame = adjusted_frame (_raw_grab_frame, event).frame;
272 _last_pointer_frame = _grab_frame;
273 _last_pointer_x = _grab_x;
275 if (_trackview_only) {
276 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
279 _last_pointer_y = _grab_y;
283 if (!_editor->cursors()->is_invalid (cursor)) {
284 /* CAIROCANVAS need a variant here that passes *cursor */
285 _cursor_ctx = CursorContext::create (*_editor, cursor);
288 if (_editor->session() && _editor->session()->transport_rolling()) {
291 _was_rolling = false;
294 switch (_editor->snap_type()) {
295 case SnapToRegionStart:
296 case SnapToRegionEnd:
297 case SnapToRegionSync:
298 case SnapToRegionBoundary:
299 _editor->build_region_boundary_cache ();
306 /** Call to end a drag `successfully'. Ungrabs item and calls
307 * subclass' finished() method.
309 * @param event GDK event, or 0.
310 * @return true if some movement occurred, otherwise false.
313 Drag::end_grab (GdkEvent* event)
315 _editor->stop_canvas_autoscroll ();
319 finished (event, _move_threshold_passed);
321 _editor->verbose_cursor()->hide ();
324 return _move_threshold_passed;
328 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
330 MusicFrame pos (0, 0);
332 if (f > _pointer_frame_offset) {
333 pos.frame = f - _pointer_frame_offset;
337 _editor->snap_to_with_modifier (pos, event);
344 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
346 return adjusted_frame (_drags->current_pointer_frame (), event, snap).frame;
350 Drag::snap_delta (guint state) const
352 if (ArdourKeyboard::indicates_snap_delta (state)) {
360 Drag::current_pointer_x() const
362 return _drags->current_pointer_x ();
366 Drag::current_pointer_y () const
368 if (!_trackview_only) {
369 return _drags->current_pointer_y ();
372 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
376 Drag::setup_snap_delta (framepos_t pos)
378 MusicFrame snap (pos, 0);
379 _editor->snap_to (snap, ARDOUR::RoundNearest, false, true);
380 _snap_delta = snap.frame - pos;
384 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
386 /* check to see if we have moved in any way that matters since the last motion event */
387 if (_move_threshold_passed &&
388 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
389 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
393 pair<framecnt_t, int> const threshold = move_threshold ();
395 bool const old_move_threshold_passed = _move_threshold_passed;
397 if (!_move_threshold_passed) {
399 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
400 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
402 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
405 if (active (_editor->mouse_mode) && _move_threshold_passed) {
407 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
409 if (old_move_threshold_passed != _move_threshold_passed) {
413 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
414 _initially_vertical = true;
416 _initially_vertical = false;
418 /** check constraints for this drag.
419 * Note that the current convention is to use "contains" for
420 * key modifiers during motion and "equals" when initiating a drag.
421 * In this case we haven't moved yet, so "equals" applies here.
423 if (Config->get_edit_mode() != Lock) {
424 if (event->motion.state & Gdk::BUTTON2_MASK) {
425 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
426 if (_constraint_pressed) {
427 _x_constrained = false;
428 _y_constrained = true;
430 _x_constrained = true;
431 _y_constrained = false;
433 } else if (_constraint_pressed) {
434 // if dragging normally, the motion is constrained to the first direction of movement.
435 if (_initially_vertical) {
436 _x_constrained = true;
437 _y_constrained = false;
439 _x_constrained = false;
440 _y_constrained = true;
444 if (event->button.state & Gdk::BUTTON2_MASK) {
445 _x_constrained = false;
447 _x_constrained = true;
449 _y_constrained = false;
453 if (!from_autoscroll) {
454 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
457 if (!_editor->autoscroll_active() || from_autoscroll) {
460 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
462 motion (event, first_move && !_starting_point_passed);
464 if (first_move && !_starting_point_passed) {
465 _starting_point_passed = true;
468 _last_pointer_x = _drags->current_pointer_x ();
469 _last_pointer_y = current_pointer_y ();
470 _last_pointer_frame = adjusted_current_frame (event, false);
480 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
488 aborted (_move_threshold_passed);
490 _editor->stop_canvas_autoscroll ();
491 _editor->verbose_cursor()->hide ();
495 Drag::show_verbose_cursor_time (framepos_t frame)
497 _editor->verbose_cursor()->set_time (frame);
498 _editor->verbose_cursor()->show ();
502 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
504 _editor->verbose_cursor()->set_duration (start, end);
505 _editor->verbose_cursor()->show ();
509 Drag::show_verbose_cursor_text (string const & text)
511 _editor->verbose_cursor()->set (text);
512 _editor->verbose_cursor()->show ();
515 boost::shared_ptr<Region>
516 Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
518 if (_editor->session()) {
519 const TempoMap& map (_editor->session()->tempo_map());
520 framecnt_t pos = grab_frame();
521 /* not that the frame rate used here can be affected by pull up/down which
524 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
525 return view->add_region (grab_frame(), len, commit);
528 return boost::shared_ptr<Region>();
531 struct PresentationInfoTimeAxisViewSorter {
532 bool operator() (TimeAxisView* a, TimeAxisView* b) {
533 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
537 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
542 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
544 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
545 as some of the regions we are dragging may be on such tracks.
548 TrackViewList track_views = _editor->track_views;
549 track_views.sort (PresentationInfoTimeAxisViewSorter ());
551 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
552 _time_axis_views.push_back (*i);
554 TimeAxisView::Children children_list = (*i)->get_child_list ();
555 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
556 _time_axis_views.push_back (j->get());
560 /* the list of views can be empty at this point if this is a region list-insert drag
563 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
564 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
567 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
571 RegionDrag::region_going_away (RegionView* v)
573 list<DraggingView>::iterator i = _views.begin ();
574 while (i != _views.end() && i->view != v) {
578 if (i != _views.end()) {
583 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
584 * or -1 if it is not found.
587 RegionDrag::find_time_axis_view (TimeAxisView* t) const
590 int const N = _time_axis_views.size ();
591 while (i < N && _time_axis_views[i] != t) {
595 if (_time_axis_views[i] != t) {
602 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
603 : RegionDrag (e, i, p, v)
605 , _ignore_video_lock (false)
606 , _last_position (0, 0)
608 , _last_pointer_time_axis_view (0)
609 , _last_pointer_layer (0)
614 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
618 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
620 Drag::start_grab (event, cursor);
621 setup_snap_delta (_last_position.frame);
623 show_verbose_cursor_time (_last_position.frame);
625 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
627 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
628 assert(_last_pointer_time_axis_view >= 0);
629 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
632 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
633 _ignore_video_lock = true;
637 /* cross track dragging seems broken here. disabled for now. */
638 _y_constrained = true;
643 RegionMotionDrag::compute_x_delta (GdkEvent const * event, MusicFrame* pending_region_position)
645 /* compute the amount of pointer motion in frames, and where
646 the region would be if we moved it by that much.
648 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
650 framecnt_t sync_offset;
653 sync_offset = _primary->region()->sync_offset (sync_dir);
655 /* we don't handle a sync point that lies before zero.
657 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position->frame >= sync_offset)) {
659 framecnt_t const sd = snap_delta (event->button.state);
660 MusicFrame sync_snap (pending_region_position->frame + (sync_dir * sync_offset) + sd, 0);
661 _editor->snap_to_with_modifier (sync_snap, event);
662 if (sync_offset == 0 && sd == 0) {
663 *pending_region_position = sync_snap;
665 pending_region_position->set (_primary->region()->adjust_to_sync (sync_snap.frame) - sd, 0);
668 *pending_region_position = _last_position;
671 if (pending_region_position->frame > max_framepos - _primary->region()->length()) {
672 *pending_region_position = _last_position;
677 bool const x_move_allowed = !_x_constrained;
679 if ((pending_region_position->frame != _last_position.frame) && x_move_allowed) {
681 /* x movement since last time (in pixels) */
682 dx = (static_cast<double> (pending_region_position->frame) - _last_position.frame) / _editor->samples_per_pixel;
684 /* total x movement */
685 framecnt_t total_dx = pending_region_position->frame;
686 if (regions_came_from_canvas()) {
687 total_dx = total_dx - grab_frame ();
690 /* check that no regions have gone off the start of the session */
691 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
692 if ((i->view->region()->position() + total_dx) < 0) {
694 *pending_region_position = _last_position;
705 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
711 const int tavsize = _time_axis_views.size();
712 const int dt = delta > 0 ? +1 : -1;
714 int target = start + delta - skip;
716 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
717 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
719 while (current >= 0 && current != target) {
721 if (current < 0 && dt < 0) {
724 if (current >= tavsize && dt > 0) {
727 if (current < 0 || current >= tavsize) {
731 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
732 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
736 if (distance_only && current == start + delta) {
744 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
746 if (_y_constrained) {
750 const int tavsize = _time_axis_views.size();
751 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
752 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
753 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
755 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
756 /* already in the drop zone */
757 if (delta_track >= 0) {
758 /* downward motion - OK if others are still not in the dropzone */
767 } else if (n >= tavsize) {
768 /* downward motion into drop zone. That's fine. */
772 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
773 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
774 /* not a track, or the wrong type */
778 double const l = i->layer + delta_layer;
780 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
781 mode to allow the user to place a region below another on layer 0.
783 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
784 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
785 If it has, the layers will be munged later anyway, so it's ok.
791 /* all regions being dragged are ok with this change */
795 struct DraggingViewSorter {
796 bool operator() (const DraggingView& a, const DraggingView& b) {
797 return a.time_axis_view < b.time_axis_view;
802 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
804 double delta_layer = 0;
805 int delta_time_axis_view = 0;
806 int current_pointer_time_axis_view = -1;
808 assert (!_views.empty ());
810 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
812 /* Find the TimeAxisView that the pointer is now over */
813 const double cur_y = current_pointer_y ();
814 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
815 TimeAxisView* tv = r.first;
817 if (!tv && cur_y < 0) {
818 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
822 /* find drop-zone y-position */
823 Coord last_track_bottom_edge;
824 last_track_bottom_edge = 0;
825 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
826 if (!(*t)->hidden()) {
827 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
832 if (tv && tv->view()) {
833 /* the mouse is over a track */
834 double layer = r.second;
836 if (first_move && tv->view()->layer_display() == Stacked) {
837 tv->view()->set_layer_display (Expanded);
840 /* Here's the current pointer position in terms of time axis view and layer */
841 current_pointer_time_axis_view = find_time_axis_view (tv);
842 assert(current_pointer_time_axis_view >= 0);
844 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
846 /* Work out the change in y */
848 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
849 if (!rtv || !rtv->is_track()) {
850 /* ignore non-tracks early on. we can't move any regions on them */
851 } else if (_last_pointer_time_axis_view < 0) {
852 /* Was in the drop-zone, now over a track.
853 * Hence it must be an upward move (from the bottom)
855 * track_index is still -1, so delta must be set to
856 * move up the correct number of tracks from the bottom.
858 * This is necessary because steps may be skipped if
859 * the bottom-most track is not a valid target and/or
860 * if there are hidden tracks at the bottom.
861 * Hence the initial offset (_ddropzone) as well as the
862 * last valid pointer position (_pdropzone) need to be
863 * taken into account.
865 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
867 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
870 /* TODO needs adjustment per DraggingView,
872 * e.g. select one region on the top-layer of a track
873 * and one region which is at the bottom-layer of another track
876 * Indicated drop-zones and layering is wrong.
877 * and may infer additional layers on the target-track
878 * (depending how many layers the original track had).
880 * Or select two regions (different layers) on a same track,
881 * move across a non-layer track.. -> layering info is lost.
882 * on drop either of the regions may be on top.
884 * Proposed solution: screw it :) well,
885 * don't use delta_layer, use an absolute value
886 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
887 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
888 * 3) iterate over all DraggingView, find the one that is over the track with most layers
889 * 4) proportionally scale layer to layers available on target
891 delta_layer = current_pointer_layer - _last_pointer_layer;
894 /* for automation lanes, there is a TimeAxisView but no ->view()
895 * if (!tv) -> dropzone
897 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
898 /* Moving into the drop-zone.. */
899 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
900 /* delta_time_axis_view may not be sufficient to move into the DZ
901 * the mouse may enter it, but it may not be a valid move due to
904 * -> remember the delta needed to move into the dropzone
906 _ddropzone = delta_time_axis_view;
907 /* ..but subtract hidden tracks (or routes) at the bottom.
908 * we silently move mover them
910 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
911 - _time_axis_views.size();
913 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
914 /* move around inside the zone.
915 * This allows to move further down until all regions are in the zone.
917 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
918 assert(ptr_y >= last_track_bottom_edge);
919 assert(_ddropzone > 0);
921 /* calculate mouse position in 'tracks' below last track. */
922 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
923 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
925 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
927 delta_time_axis_view = dzpos - _pdropzone;
928 } else if (dzpos < _pdropzone && _ndropzone > 0) {
929 // move up inside the DZ
930 delta_time_axis_view = dzpos - _pdropzone;
934 /* Work out the change in x */
935 MusicFrame pending_region_position (0, 0);
936 double const x_delta = compute_x_delta (event, &pending_region_position);
937 _last_position = pending_region_position;
939 /* calculate hidden tracks in current y-axis delta */
941 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
942 /* The mouse is more than one track below the dropzone.
943 * distance calculation is not needed (and would not work, either
944 * because the dropzone is "packed").
946 * Except when [partially] moving regions out of dropzone in a large step.
947 * (the mouse may or may not remain in the DZ)
948 * Hidden tracks at the bottom of the TAV need to be skipped.
950 * This also handles the case if the mouse entered the DZ
951 * in a large step (exessive delta), either due to fast-movement,
952 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
954 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
955 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
957 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
958 -_time_axis_views.size() - dt;
961 else if (_last_pointer_time_axis_view < 0) {
962 /* Moving out of the zone. Check for hidden tracks at the bottom. */
963 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
964 -_time_axis_views.size() - delta_time_axis_view;
966 /* calculate hidden tracks that are skipped by the pointer movement */
967 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
968 - _last_pointer_time_axis_view
969 - delta_time_axis_view;
972 /* Verify change in y */
973 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
974 /* this y movement is not allowed, so do no y movement this time */
975 delta_time_axis_view = 0;
980 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
981 /* haven't reached next snap point, and we're not switching
982 trackviews nor layers. nothing to do.
987 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
988 PlaylistDropzoneMap playlist_dropzone_map;
989 _ndropzone = 0; // number of elements currently in the dropzone
992 /* sort views by time_axis.
993 * This retains track order in the dropzone, regardless
994 * of actual selection order
996 _views.sort (DraggingViewSorter());
998 /* count number of distinct tracks of all regions
999 * being dragged, used for dropzone.
1001 int prev_track = -1;
1002 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1003 if (i->time_axis_view != prev_track) {
1004 prev_track = i->time_axis_view;
1010 _views.back().time_axis_view -
1011 _views.front().time_axis_view;
1013 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1014 - _views.back().time_axis_view;
1016 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1020 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1022 RegionView* rv = i->view;
1027 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1034 /* reparent the regionview into a group above all
1038 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1039 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1040 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1041 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1042 /* move the item so that it continues to appear at the
1043 same location now that its parent has changed.
1045 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1048 /* If we have moved tracks, we'll fudge the layer delta so that the
1049 region gets moved back onto layer 0 on its new track; this avoids
1050 confusion when dragging regions from non-zero layers onto different
1053 double this_delta_layer = delta_layer;
1054 if (delta_time_axis_view != 0) {
1055 this_delta_layer = - i->layer;
1058 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1060 int track_index = i->time_axis_view + this_delta_time_axis_view;
1061 assert(track_index >= 0);
1063 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1064 /* Track is in the Dropzone */
1066 i->time_axis_view = track_index;
1067 assert(i->time_axis_view >= (int) _time_axis_views.size());
1070 double yposition = 0;
1071 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1072 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1075 /* store index of each new playlist as a negative count, starting at -1 */
1077 if (pdz == playlist_dropzone_map.end()) {
1078 /* compute where this new track (which doesn't exist yet) will live
1081 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1083 /* How high is this region view ? */
1085 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1086 ArdourCanvas::Rect bbox;
1089 bbox = obbox.get ();
1092 last_track_bottom_edge += bbox.height();
1094 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1097 yposition = pdz->second;
1100 /* values are zero or negative, hence the use of min() */
1101 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1104 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1106 mrv->apply_note_range (60, 71, true);
1110 /* The TimeAxisView that this region is now over */
1111 TimeAxisView* current_tv = _time_axis_views[track_index];
1113 /* Ensure it is moved from stacked -> expanded if appropriate */
1114 if (current_tv->view()->layer_display() == Stacked) {
1115 current_tv->view()->set_layer_display (Expanded);
1118 /* We're only allowed to go -ve in layer on Expanded views */
1119 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1120 this_delta_layer = - i->layer;
1124 rv->set_height (current_tv->view()->child_height ());
1126 /* Update show/hidden status as the region view may have come from a hidden track,
1127 or have moved to one.
1129 if (current_tv->hidden ()) {
1130 rv->get_canvas_group()->hide ();
1132 rv->get_canvas_group()->show ();
1135 /* Update the DraggingView */
1136 i->time_axis_view = track_index;
1137 i->layer += this_delta_layer;
1140 _editor->mouse_brush_insert_region (rv, pending_region_position.frame);
1144 /* Get the y coordinate of the top of the track that this region is now over */
1145 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1147 /* And adjust for the layer that it should be on */
1148 StreamView* cv = current_tv->view ();
1149 switch (cv->layer_display ()) {
1153 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1156 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1160 /* need to get the parent of the regionview
1161 * canvas group and get its position in
1162 * equivalent coordinate space as the trackview
1163 * we are now dragging over.
1166 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1170 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1172 MidiStreamView* msv;
1173 if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
1174 mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
1179 /* Now move the region view */
1180 rv->move (x_delta, y_delta);
1182 } /* foreach region */
1184 _total_x_delta += x_delta;
1186 if (x_delta != 0 && !_brushing) {
1187 show_verbose_cursor_time (_last_position.frame);
1190 /* keep track of pointer movement */
1192 /* the pointer is currently over a time axis view */
1194 if (_last_pointer_time_axis_view < 0) {
1195 /* last motion event was not over a time axis view
1196 * or last y-movement out of the dropzone was not valid
1199 if (delta_time_axis_view < 0) {
1200 /* in the drop zone, moving up */
1202 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1203 * We do not use negative _last_pointer_time_axis_view because
1204 * the dropzone is "packed" (the actual track offset is ignored)
1206 * As opposed to the actual number
1207 * of elements in the dropzone (_ndropzone)
1208 * _pdropzone is not constrained. This is necessary
1209 * to allow moving multiple regions with y-distance
1212 * There can be 0 elements in the dropzone,
1213 * even though the drag-pointer is inside the DZ.
1216 * [ Audio-track, Midi-track, Audio-track, DZ ]
1217 * move regions from both audio tracks at the same time into the
1218 * DZ by grabbing the region in the bottom track.
1220 assert(current_pointer_time_axis_view >= 0);
1221 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1225 /* only move out of the zone if the movement is OK */
1226 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1227 assert(delta_time_axis_view < 0);
1228 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1229 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1230 * the current position can be calculated as follows:
1232 // a well placed oofus attack can still throw this off.
1233 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1234 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1237 /* last motion event was also over a time axis view */
1238 _last_pointer_time_axis_view += delta_time_axis_view;
1239 assert(_last_pointer_time_axis_view >= 0);
1244 /* the pointer is not over a time axis view */
1245 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1246 _pdropzone += delta_time_axis_view - delta_skip;
1247 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1250 _last_pointer_layer += delta_layer;
1254 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1256 if (_copy && first_move) {
1257 if (_x_constrained && !_brushing) {
1258 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1259 } else if (!_brushing) {
1260 _editor->begin_reversible_command (Operations::region_copy);
1261 } else if (_brushing) {
1262 _editor->begin_reversible_command (Operations::drag_region_brush);
1264 /* duplicate the regionview(s) and region(s) */
1266 list<DraggingView> new_regionviews;
1268 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1270 RegionView* rv = i->view;
1271 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1272 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1274 const boost::shared_ptr<const Region> original = rv->region();
1275 boost::shared_ptr<Region> region_copy;
1277 region_copy = RegionFactory::create (original, true);
1279 /* need to set this so that the drop zone code can work. This doesn't
1280 actually put the region into the playlist, but just sets a weak pointer
1283 region_copy->set_playlist (original->playlist());
1287 boost::shared_ptr<AudioRegion> audioregion_copy
1288 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1290 nrv = new AudioRegionView (*arv, audioregion_copy);
1292 boost::shared_ptr<MidiRegion> midiregion_copy
1293 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1294 nrv = new MidiRegionView (*mrv, midiregion_copy);
1299 nrv->get_canvas_group()->show ();
1300 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1302 /* swap _primary to the copy */
1304 if (rv == _primary) {
1308 /* ..and deselect the one we copied */
1310 rv->set_selected (false);
1313 if (!new_regionviews.empty()) {
1315 /* reflect the fact that we are dragging the copies */
1317 _views = new_regionviews;
1319 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1322 } else if (!_copy && first_move) {
1323 if (_x_constrained && !_brushing) {
1324 _editor->begin_reversible_command (_("fixed time region drag"));
1325 } else if (!_brushing) {
1326 _editor->begin_reversible_command (Operations::region_drag);
1327 } else if (_brushing) {
1328 _editor->begin_reversible_command (Operations::drag_region_brush);
1331 RegionMotionDrag::motion (event, first_move);
1335 RegionMotionDrag::finished (GdkEvent *, bool)
1337 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1338 if (!(*i)->view()) {
1342 if ((*i)->view()->layer_display() == Expanded) {
1343 (*i)->view()->set_layer_display (Stacked);
1349 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1351 RegionMotionDrag::finished (ev, movement_occurred);
1353 if (!movement_occurred) {
1357 if (was_double_click() && !_views.empty()) {
1358 DraggingView dv = _views.front();
1359 _editor->edit_region (dv.view);
1365 assert (!_views.empty ());
1367 /* We might have hidden region views so that they weren't visible during the drag
1368 (when they have been reparented). Now everything can be shown again, as region
1369 views are back in their track parent groups.
1371 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1372 i->view->get_canvas_group()->show ();
1375 bool const changed_position = (_last_position.frame != _primary->region()->position());
1376 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1400 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1402 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1405 TimeAxisView* tav = 0;
1407 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1408 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1409 uint32_t output_chan = region->n_channels();
1410 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1411 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1413 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1414 tav =_editor->axis_view_from_stripable (audio_tracks.front());
1416 ChanCount one_midi_port (DataType::MIDI, 1);
1417 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1418 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port,
1419 Config->get_strict_io () || Profile->get_mixbus (),
1420 boost::shared_ptr<ARDOUR::PluginInfo>(),
1421 (ARDOUR::Plugin::PresetRecord*) 0,
1422 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1423 tav = _editor->axis_view_from_stripable (midi_tracks.front());
1427 tav->set_height (original->current_height());
1430 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1433 return dynamic_cast<RouteTimeAxisView*> (tav);
1437 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, MusicFrame last_position, int32_t const ev_state)
1439 RegionSelection new_views;
1440 PlaylistSet modified_playlists;
1441 RouteTimeAxisView* new_time_axis_view = 0;
1442 framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1444 TempoMap& tmap (_editor->session()->tempo_map());
1445 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1446 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1449 /* all changes were made during motion event handlers */
1451 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1455 _editor->commit_reversible_command ();
1459 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1460 PlaylistMapping playlist_mapping;
1462 /* insert the regions into their new playlists */
1463 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1465 RouteTimeAxisView* dest_rtv = 0;
1467 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1471 MusicFrame where (0, 0);
1472 double quarter_note;
1474 if (changed_position && !_x_constrained) {
1475 where.set (i->view->region()->position() - drag_delta, 0);
1476 quarter_note = i->view->region()->quarter_note() - qn_delta;
1478 /* region has not moved - divisor will not affect musical pos */
1479 where.set (i->view->region()->position(), 0);
1480 quarter_note = i->view->region()->quarter_note();
1483 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1484 /* dragged to drop zone */
1486 PlaylistMapping::iterator pm;
1488 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1489 /* first region from this original playlist: create a new track */
1490 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1491 if(!new_time_axis_view) {
1495 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1496 dest_rtv = new_time_axis_view;
1498 /* we already created a new track for regions from this playlist, use it */
1499 dest_rtv = pm->second;
1502 /* destination time axis view is the one we dragged to */
1503 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1506 if (dest_rtv != 0) {
1507 RegionView* new_view;
1508 if (i->view == _primary && !_x_constrained) {
1509 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, last_position, last_pos_qn,
1510 modified_playlists, true);
1512 if (i->view->region()->position_lock_style() == AudioTime) {
1513 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1514 modified_playlists);
1516 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1517 modified_playlists, true);
1521 if (new_view != 0) {
1522 new_views.push_back (new_view);
1526 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1527 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1530 list<DraggingView>::const_iterator next = i;
1536 /* If we've created new regions either by copying or moving
1537 to a new track, we want to replace the old selection with the new ones
1540 if (new_views.size() > 0) {
1541 _editor->selection->set (new_views);
1544 /* write commands for the accumulated diffs for all our modified playlists */
1545 add_stateful_diff_commands_for_playlists (modified_playlists);
1547 _editor->commit_reversible_command ();
1551 RegionMoveDrag::finished_no_copy (
1552 bool const changed_position,
1553 bool const changed_tracks,
1554 MusicFrame last_position,
1555 int32_t const ev_state
1558 RegionSelection new_views;
1559 PlaylistSet modified_playlists;
1560 PlaylistSet frozen_playlists;
1561 set<RouteTimeAxisView*> views_to_update;
1562 RouteTimeAxisView* new_time_axis_view = 0;
1563 framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1565 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1566 PlaylistMapping playlist_mapping;
1568 TempoMap& tmap (_editor->session()->tempo_map());
1569 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1570 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1572 std::set<boost::shared_ptr<const Region> > uniq;
1573 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1575 RegionView* rv = i->view;
1576 RouteTimeAxisView* dest_rtv = 0;
1578 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1583 if (uniq.find (rv->region()) != uniq.end()) {
1584 /* prevent duplicate moves when selecting regions from shared playlists */
1588 uniq.insert(rv->region());
1590 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1591 /* dragged to drop zone */
1593 PlaylistMapping::iterator pm;
1595 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1596 /* first region from this original playlist: create a new track */
1597 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1598 if(!new_time_axis_view) { // New track creation failed
1602 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1603 dest_rtv = new_time_axis_view;
1605 /* we already created a new track for regions from this playlist, use it */
1606 dest_rtv = pm->second;
1610 /* destination time axis view is the one we dragged to */
1611 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1616 double const dest_layer = i->layer;
1618 views_to_update.insert (dest_rtv);
1620 MusicFrame where (0, 0);
1621 double quarter_note;
1623 if (changed_position && !_x_constrained) {
1624 where.set (rv->region()->position() - drag_delta, 0);
1625 quarter_note = i->view->region()->quarter_note() - qn_delta;
1627 where.set (rv->region()->position(), 0);
1628 quarter_note = i->view->region()->quarter_note();
1631 if (changed_tracks) {
1633 /* insert into new playlist */
1634 RegionView* new_view;
1635 if (rv == _primary) {
1636 new_view = insert_region_into_playlist (
1637 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, last_position, last_pos_qn,
1638 modified_playlists, true
1641 if (rv->region()->position_lock_style() == AudioTime) {
1643 new_view = insert_region_into_playlist (
1644 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1648 new_view = insert_region_into_playlist (
1649 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1650 modified_playlists, true
1655 if (new_view == 0) {
1660 new_views.push_back (new_view);
1662 /* remove from old playlist */
1664 /* the region that used to be in the old playlist is not
1665 moved to the new one - we use a copy of it. as a result,
1666 any existing editor for the region should no longer be
1669 rv->hide_region_editor();
1672 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1676 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1678 /* this movement may result in a crossfade being modified, or a layering change,
1679 so we need to get undo data from the playlist as well as the region.
1682 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1684 playlist->clear_changes ();
1687 rv->region()->clear_changes ();
1690 motion on the same track. plonk the previously reparented region
1691 back to its original canvas group (its streamview).
1692 No need to do anything for copies as they are fake regions which will be deleted.
1695 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1696 rv->get_canvas_group()->set_y_position (i->initial_y);
1699 /* just change the model */
1700 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1701 playlist->set_layer (rv->region(), dest_layer);
1704 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1706 r = frozen_playlists.insert (playlist);
1709 playlist->freeze ();
1711 if (rv == _primary) {
1712 rv->region()->set_position (where.frame, last_position.division);
1714 if (rv->region()->position_lock_style() == AudioTime) {
1715 /* move by frame offset */
1716 rv->region()->set_position (where.frame, 0);
1718 /* move by music offset */
1719 rv->region()->set_position_music (rv->region()->quarter_note() - qn_delta);
1722 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1725 if (changed_tracks) {
1727 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1728 was selected in all of them, then removing it from a playlist will have removed all
1729 trace of it from _views (i.e. there were N regions selected, we removed 1,
1730 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1731 corresponding regionview, and _views is now empty).
1733 This could have invalidated any and all iterators into _views.
1735 The heuristic we use here is: if the region selection is empty, break out of the loop
1736 here. if the region selection is not empty, then restart the loop because we know that
1737 we must have removed at least the region(view) we've just been working on as well as any
1738 that we processed on previous iterations.
1740 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1741 we can just iterate.
1745 if (_views.empty()) {
1756 /* If we've created new regions either by copying or moving
1757 to a new track, we want to replace the old selection with the new ones
1760 if (new_views.size() > 0) {
1761 _editor->selection->set (new_views);
1764 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1768 /* write commands for the accumulated diffs for all our modified playlists */
1769 add_stateful_diff_commands_for_playlists (modified_playlists);
1770 /* applies to _brushing */
1771 _editor->commit_reversible_command ();
1773 /* We have futzed with the layering of canvas items on our streamviews.
1774 If any region changed layer, this will have resulted in the stream
1775 views being asked to set up their region views, and all will be well.
1776 If not, we might now have badly-ordered region views. Ask the StreamViews
1777 involved to sort themselves out, just in case.
1780 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1781 (*i)->view()->playlist_layered ((*i)->track ());
1785 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1786 * @param region Region to remove.
1787 * @param playlist playlist To remove from.
1788 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1789 * that clear_changes () is only called once per playlist.
1792 RegionMoveDrag::remove_region_from_playlist (
1793 boost::shared_ptr<Region> region,
1794 boost::shared_ptr<Playlist> playlist,
1795 PlaylistSet& modified_playlists
1798 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1801 playlist->clear_changes ();
1804 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1808 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1809 * clearing the playlist's diff history first if necessary.
1810 * @param region Region to insert.
1811 * @param dest_rtv Destination RouteTimeAxisView.
1812 * @param dest_layer Destination layer.
1813 * @param where Destination position.
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.
1816 * @return New RegionView, or 0 if no insert was performed.
1819 RegionMoveDrag::insert_region_into_playlist (
1820 boost::shared_ptr<Region> region,
1821 RouteTimeAxisView* dest_rtv,
1824 double quarter_note,
1825 PlaylistSet& modified_playlists,
1829 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1830 if (!dest_playlist) {
1834 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1835 _new_region_view = 0;
1836 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1838 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1839 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1841 dest_playlist->clear_changes ();
1844 dest_playlist->add_region (region, where.frame, 1.0, false, where.division, quarter_note, true);
1846 dest_playlist->add_region (region, where.frame, 1.0, false, where.division);
1849 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1850 dest_playlist->set_layer (region, dest_layer);
1855 assert (_new_region_view);
1857 return _new_region_view;
1861 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1863 _new_region_view = rv;
1867 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1869 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1870 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1872 _editor->session()->add_command (c);
1881 RegionMoveDrag::aborted (bool movement_occurred)
1885 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1886 list<DraggingView>::const_iterator next = i;
1895 RegionMotionDrag::aborted (movement_occurred);
1900 RegionMotionDrag::aborted (bool)
1902 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1904 StreamView* sview = (*i)->view();
1907 if (sview->layer_display() == Expanded) {
1908 sview->set_layer_display (Stacked);
1913 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1914 RegionView* rv = i->view;
1915 TimeAxisView* tv = &(rv->get_time_axis_view ());
1916 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1918 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1919 rv->get_canvas_group()->set_y_position (0);
1921 rv->move (-_total_x_delta, 0);
1922 rv->set_height (rtv->view()->child_height ());
1926 /** @param b true to brush, otherwise false.
1927 * @param c true to make copies of the regions being moved, otherwise false.
1929 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1930 : RegionMotionDrag (e, i, p, v, b)
1932 , _new_region_view (0)
1934 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1937 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1938 if (rtv && rtv->is_track()) {
1939 speed = rtv->track()->speed ();
1942 _last_position = MusicFrame (static_cast<framepos_t> (_primary->region()->position() / speed), 0);
1946 RegionMoveDrag::setup_pointer_frame_offset ()
1948 _pointer_frame_offset = raw_grab_frame() - _last_position.frame;
1951 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1952 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1954 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1956 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1957 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1959 _primary = v->view()->create_region_view (r, false, false);
1961 _primary->get_canvas_group()->show ();
1962 _primary->set_position (pos, 0);
1963 _views.push_back (DraggingView (_primary, this, v));
1965 _last_position = MusicFrame (pos, 0);
1967 _item = _primary->get_canvas_group ();
1971 RegionInsertDrag::finished (GdkEvent * event, bool)
1973 int pos = _views.front().time_axis_view;
1974 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1976 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1978 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1979 _primary->get_canvas_group()->set_y_position (0);
1981 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1983 _editor->begin_reversible_command (Operations::insert_region);
1984 playlist->clear_changes ();
1985 playlist->add_region (_primary->region (), _last_position.frame, _editor->get_grid_music_divisions (event->button.state));
1987 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1988 if (Config->get_edit_mode() == Ripple) {
1989 playlist->ripple (_last_position.frame, _primary->region()->length(), _primary->region());
1992 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1993 _editor->commit_reversible_command ();
2001 RegionInsertDrag::aborted (bool)
2008 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2009 : RegionMoveDrag (e, i, p, v, false, false)
2011 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
2014 struct RegionSelectionByPosition {
2015 bool operator() (RegionView*a, RegionView* b) {
2016 return a->region()->position () < b->region()->position();
2021 RegionSpliceDrag::motion (GdkEvent* event, bool)
2023 /* Which trackview is this ? */
2025 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2026 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2028 /* The region motion is only processed if the pointer is over
2032 if (!tv || !tv->is_track()) {
2033 /* To make sure we hide the verbose canvas cursor when the mouse is
2034 not held over an audio track.
2036 _editor->verbose_cursor()->hide ();
2039 _editor->verbose_cursor()->show ();
2044 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2050 RegionSelection copy;
2051 _editor->selection->regions.by_position(copy);
2053 framepos_t const pf = adjusted_current_frame (event);
2055 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2057 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2063 boost::shared_ptr<Playlist> playlist;
2065 if ((playlist = atv->playlist()) == 0) {
2069 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2074 if (pf < (*i)->region()->last_frame() + 1) {
2078 if (pf > (*i)->region()->first_frame()) {
2084 playlist->shuffle ((*i)->region(), dir);
2089 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2091 RegionMoveDrag::finished (event, movement_occurred);
2095 RegionSpliceDrag::aborted (bool)
2105 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2108 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2110 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2111 RegionSelection to_ripple;
2112 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2113 if ((*i)->position() >= where) {
2114 to_ripple.push_back (rtv->view()->find_view(*i));
2118 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2119 if (!exclude.contains (*i)) {
2120 // the selection has already been added to _views
2122 if (drag_in_progress) {
2123 // do the same things that RegionMotionDrag::motion does when
2124 // first_move is true, for the region views that we're adding
2125 // to _views this time
2128 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2129 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2130 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2131 rvg->reparent (_editor->_drag_motion_group);
2133 // we only need to move in the y direction
2134 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2139 _views.push_back (DraggingView (*i, this, tav));
2145 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2148 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2149 // we added all the regions after the selection
2151 std::list<DraggingView>::iterator to_erase = i++;
2152 if (!_editor->selection->regions.contains (to_erase->view)) {
2153 // restore the non-selected regions to their original playlist & positions,
2154 // and then ripple them back by the length of the regions that were dragged away
2155 // do the same things as RegionMotionDrag::aborted
2157 RegionView *rv = to_erase->view;
2158 TimeAxisView* tv = &(rv->get_time_axis_view ());
2159 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2162 // plonk them back onto their own track
2163 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2164 rv->get_canvas_group()->set_y_position (0);
2168 // move the underlying region to match the view
2169 rv->region()->set_position (rv->region()->position() + amount);
2171 // restore the view to match the underlying region's original position
2172 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2175 rv->set_height (rtv->view()->child_height ());
2176 _views.erase (to_erase);
2182 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2184 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2186 return allow_moves_across_tracks;
2194 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2195 : RegionMoveDrag (e, i, p, v, false, false)
2197 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2198 // compute length of selection
2199 RegionSelection selected_regions = _editor->selection->regions;
2200 selection_length = selected_regions.end_frame() - selected_regions.start();
2202 // we'll only allow dragging to another track in ripple mode if all the regions
2203 // being dragged start off on the same track
2204 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2207 exclude = new RegionList;
2208 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2209 exclude->push_back((*i)->region());
2212 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2213 RegionSelection copy;
2214 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2216 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2217 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2219 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2220 // find ripple start point on each applicable playlist
2221 RegionView *first_selected_on_this_track = NULL;
2222 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2223 if ((*i)->region()->playlist() == (*pi)) {
2224 // region is on this playlist - it's the first, because they're sorted
2225 first_selected_on_this_track = *i;
2229 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2230 add_all_after_to_views (
2231 &first_selected_on_this_track->get_time_axis_view(),
2232 first_selected_on_this_track->region()->position(),
2233 selected_regions, false);
2236 if (allow_moves_across_tracks) {
2237 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2245 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2247 /* Which trackview is this ? */
2249 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2250 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2252 /* The region motion is only processed if the pointer is over
2256 if (!tv || !tv->is_track()) {
2257 /* To make sure we hide the verbose canvas cursor when the mouse is
2258 not held over an audiotrack.
2260 _editor->verbose_cursor()->hide ();
2264 framepos_t where = adjusted_current_frame (event);
2265 assert (where >= 0);
2266 MusicFrame after (0, 0);
2267 double delta = compute_x_delta (event, &after);
2269 framecnt_t amount = _editor->pixel_to_sample (delta);
2271 if (allow_moves_across_tracks) {
2272 // all the originally selected regions were on the same track
2274 framecnt_t adjust = 0;
2275 if (prev_tav && tv != prev_tav) {
2276 // dragged onto a different track
2277 // remove the unselected regions from _views, restore them to their original positions
2278 // and add the regions after the drop point on the new playlist to _views instead.
2279 // undo the effect of rippling the previous playlist, and include the effect of removing
2280 // the dragged region(s) from this track
2282 remove_unselected_from_views (prev_amount, false);
2283 // ripple previous playlist according to the regions that have been removed onto the new playlist
2284 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2287 // move just the selected regions
2288 RegionMoveDrag::motion(event, first_move);
2290 // ensure that the ripple operation on the new playlist inserts selection_length time
2291 adjust = selection_length;
2292 // ripple the new current playlist
2293 tv->playlist()->ripple (where, amount+adjust, exclude);
2295 // add regions after point where drag entered this track to subsequent ripples
2296 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2299 // motion on same track
2300 RegionMoveDrag::motion(event, first_move);
2304 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2305 prev_position = where;
2307 // selection encompasses multiple tracks - just drag
2308 // cross-track drags are forbidden
2309 RegionMoveDrag::motion(event, first_move);
2312 if (!_x_constrained) {
2313 prev_amount += amount;
2316 _last_position = after;
2320 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2322 if (!movement_occurred) {
2326 if (was_double_click() && !_views.empty()) {
2327 DraggingView dv = _views.front();
2328 _editor->edit_region (dv.view);
2334 _editor->begin_reversible_command(_("Ripple drag"));
2336 // remove the regions being rippled from the dragging view, updating them to
2337 // their new positions
2338 remove_unselected_from_views (prev_amount, true);
2340 if (allow_moves_across_tracks) {
2342 // if regions were dragged across tracks, we've rippled any later
2343 // regions on the track the regions were dragged off, so we need
2344 // to add the original track to the undo record
2345 orig_tav->playlist()->clear_changes();
2346 vector<Command*> cmds;
2347 orig_tav->playlist()->rdiff (cmds);
2348 _editor->session()->add_commands (cmds);
2350 if (prev_tav && prev_tav != orig_tav) {
2351 prev_tav->playlist()->clear_changes();
2352 vector<Command*> cmds;
2353 prev_tav->playlist()->rdiff (cmds);
2354 _editor->session()->add_commands (cmds);
2357 // selection spanned multiple tracks - all will need adding to undo record
2359 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2360 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2362 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2363 (*pi)->clear_changes();
2364 vector<Command*> cmds;
2365 (*pi)->rdiff (cmds);
2366 _editor->session()->add_commands (cmds);
2370 // other modified playlists are added to undo by RegionMoveDrag::finished()
2371 RegionMoveDrag::finished (event, movement_occurred);
2372 _editor->commit_reversible_command();
2376 RegionRippleDrag::aborted (bool movement_occurred)
2378 RegionMoveDrag::aborted (movement_occurred);
2383 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2385 _view (dynamic_cast<MidiTimeAxisView*> (v))
2387 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2393 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2396 _editor->begin_reversible_command (_("create region"));
2397 _region = add_midi_region (_view, false);
2398 _view->playlist()->freeze ();
2401 framepos_t const f = adjusted_current_frame (event);
2402 if (f < grab_frame()) {
2403 _region->set_initial_position (f);
2406 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2407 so that if this region is duplicated, its duplicate starts on
2408 a snap point rather than 1 frame after a snap point. Otherwise things get
2409 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2410 place snapped notes at the start of the region.
2413 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2414 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2420 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2422 if (!movement_occurred) {
2423 add_midi_region (_view, true);
2425 _view->playlist()->thaw ();
2426 _editor->commit_reversible_command();
2431 RegionCreateDrag::aborted (bool)
2434 _view->playlist()->thaw ();
2440 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2445 , _was_selected (false)
2448 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2452 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2454 Gdk::Cursor* cursor;
2455 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2457 float x_fraction = cnote->mouse_x_fraction ();
2459 if (x_fraction > 0.0 && x_fraction < 0.25) {
2460 cursor = _editor->cursors()->left_side_trim;
2463 cursor = _editor->cursors()->right_side_trim;
2467 Drag::start_grab (event, cursor);
2469 region = &cnote->region_view();
2472 temp = region->snap_to_pixel (cnote->x0 (), true);
2473 _snap_delta = temp - cnote->x0 ();
2477 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2482 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2483 if (ms.size() > 1) {
2484 /* has to be relative, may make no sense otherwise */
2488 if (!(_was_selected = cnote->selected())) {
2490 /* tertiary-click means extend selection - we'll do that on button release,
2491 so don't add it here, because otherwise we make it hard to figure
2492 out the "extend-to" range.
2495 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2498 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2501 region->note_selected (cnote, true);
2503 _editor->get_selection().clear_points();
2504 region->unique_select (cnote);
2511 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2513 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2515 _editor->begin_reversible_command (_("resize notes"));
2517 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2518 MidiRegionSelection::iterator next;
2521 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2523 mrv->begin_resizing (at_front);
2529 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2530 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2532 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2536 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2538 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2539 if (_editor->snap_mode () != SnapOff) {
2543 if (_editor->snap_mode () == SnapOff) {
2545 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2546 if (apply_snap_delta) {
2552 if (apply_snap_delta) {
2556 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2562 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2564 if (!movement_occurred) {
2565 /* no motion - select note */
2566 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2567 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2568 _editor->current_mouse_mode() == Editing::MouseDraw) {
2570 bool changed = false;
2572 if (_was_selected) {
2573 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2575 region->note_deselected (cnote);
2578 _editor->get_selection().clear_points();
2579 region->unique_select (cnote);
2583 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2584 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2586 if (!extend && !add && region->selection_size() > 1) {
2587 _editor->get_selection().clear_points();
2588 region->unique_select (cnote);
2590 } else if (extend) {
2591 region->note_selected (cnote, true, true);
2594 /* it was added during button press */
2600 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2601 _editor->commit_reversible_selection_op();
2608 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2609 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2610 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2612 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2615 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2617 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2618 if (_editor->snap_mode () != SnapOff) {
2622 if (_editor->snap_mode () == SnapOff) {
2624 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2625 if (apply_snap_delta) {
2631 if (apply_snap_delta) {
2635 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2639 _editor->commit_reversible_command ();
2643 NoteResizeDrag::aborted (bool)
2645 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2646 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2647 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2649 mrv->abort_resizing ();
2654 AVDraggingView::AVDraggingView (RegionView* v)
2657 initial_position = v->region()->position ();
2660 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2663 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2666 TrackViewList empty;
2668 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2669 std::list<RegionView*> views = rs.by_layer();
2672 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2673 RegionView* rv = (*i);
2674 if (!rv->region()->video_locked()) {
2677 if (rv->region()->locked()) {
2680 _views.push_back (AVDraggingView (rv));
2685 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2687 Drag::start_grab (event);
2688 if (_editor->session() == 0) {
2692 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2698 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2702 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2703 _max_backwards_drag = (
2704 ARDOUR_UI::instance()->video_timeline->get_duration()
2705 + ARDOUR_UI::instance()->video_timeline->get_offset()
2706 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2709 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2710 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2711 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2714 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2717 Timecode::Time timecode;
2718 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2719 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);
2720 show_verbose_cursor_text (buf);
2724 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2726 if (_editor->session() == 0) {
2729 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2733 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2737 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2738 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2740 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2741 dt = - _max_backwards_drag;
2744 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2745 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2747 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2748 RegionView* rv = i->view;
2749 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2752 rv->region()->clear_changes ();
2753 rv->region()->suspend_property_changes();
2755 rv->region()->set_position(i->initial_position + dt);
2756 rv->region_changed(ARDOUR::Properties::position);
2759 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2760 Timecode::Time timecode;
2761 Timecode::Time timediff;
2763 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2764 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2765 snprintf (buf, sizeof (buf),
2766 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2767 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2768 , _("Video Start:"),
2769 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2771 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2773 show_verbose_cursor_text (buf);
2777 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2779 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2786 if (!movement_occurred || ! _editor->session()) {
2790 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2792 _editor->begin_reversible_command (_("Move Video"));
2794 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2795 ARDOUR_UI::instance()->video_timeline->save_undo();
2796 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2797 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2799 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2800 i->view->drag_end();
2801 i->view->region()->resume_property_changes ();
2803 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2806 _editor->session()->maybe_update_session_range(
2807 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2808 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2812 _editor->commit_reversible_command ();
2816 VideoTimeLineDrag::aborted (bool)
2818 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2821 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2822 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2824 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2825 i->view->region()->resume_property_changes ();
2826 i->view->region()->set_position(i->initial_position);
2830 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2831 : RegionDrag (e, i, p, v)
2832 , _operation (StartTrim)
2833 , _preserve_fade_anchor (preserve_fade_anchor)
2834 , _jump_position_when_done (false)
2836 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2840 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2843 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2844 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2846 if (tv && tv->is_track()) {
2847 speed = tv->track()->speed();
2850 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2851 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2852 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2854 framepos_t const pf = adjusted_current_frame (event);
2855 setup_snap_delta (region_start);
2857 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2858 /* Move the contents of the region around without changing the region bounds */
2859 _operation = ContentsTrim;
2860 Drag::start_grab (event, _editor->cursors()->trimmer);
2862 /* These will get overridden for a point trim.*/
2863 if (pf < (region_start + region_length/2)) {
2864 /* closer to front */
2865 _operation = StartTrim;
2866 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2867 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2869 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2873 _operation = EndTrim;
2874 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2875 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2877 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2881 /* jump trim disabled for now
2882 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2883 _jump_position_when_done = true;
2887 switch (_operation) {
2889 show_verbose_cursor_time (region_start);
2892 show_verbose_cursor_duration (region_start, region_end);
2895 show_verbose_cursor_time (pf);
2899 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2900 i->view->region()->suspend_property_changes ();
2905 TrimDrag::motion (GdkEvent* event, bool first_move)
2907 RegionView* rv = _primary;
2910 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2911 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2912 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2913 frameoffset_t frame_delta = 0;
2915 if (tv && tv->is_track()) {
2916 speed = tv->track()->speed();
2918 MusicFrame adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2919 framecnt_t dt = adj_frame.frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2925 switch (_operation) {
2927 trim_type = "Region start trim";
2930 trim_type = "Region end trim";
2933 trim_type = "Region content trim";
2940 _editor->begin_reversible_command (trim_type);
2942 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2943 RegionView* rv = i->view;
2944 rv->region()->playlist()->clear_owned_changes ();
2946 if (_operation == StartTrim) {
2947 rv->trim_front_starting ();
2950 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2953 arv->temporarily_hide_envelope ();
2957 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2958 insert_result = _editor->motion_frozen_playlists.insert (pl);
2960 if (insert_result.second) {
2966 bool non_overlap_trim = false;
2968 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2969 non_overlap_trim = true;
2972 /* contstrain trim to fade length */
2973 if (_preserve_fade_anchor) {
2974 switch (_operation) {
2976 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2977 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2979 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2980 if (ar->locked()) continue;
2981 framecnt_t len = ar->fade_in()->back()->when;
2982 if (len < dt) dt = min(dt, len);
2986 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2987 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2989 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2990 if (ar->locked()) continue;
2991 framecnt_t len = ar->fade_out()->back()->when;
2992 if (len < -dt) dt = max(dt, -len);
3000 switch (_operation) {
3002 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3003 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
3004 , adj_frame.division);
3006 if (changed && _preserve_fade_anchor) {
3007 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3009 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3010 framecnt_t len = ar->fade_in()->back()->when;
3011 framecnt_t diff = ar->first_frame() - i->initial_position;
3012 framepos_t new_length = len - diff;
3013 i->anchored_fade_length = min (ar->length(), new_length);
3014 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
3015 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
3022 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3023 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, adj_frame.division);
3024 if (changed && _preserve_fade_anchor) {
3025 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3027 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3028 framecnt_t len = ar->fade_out()->back()->when;
3029 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
3030 framepos_t new_length = len + diff;
3031 i->anchored_fade_length = min (ar->length(), new_length);
3032 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
3033 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3041 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3043 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3044 i->view->move_contents (frame_delta);
3050 switch (_operation) {
3052 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3055 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3058 // show_verbose_cursor_time (frame_delta);
3064 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3066 if (movement_occurred) {
3067 motion (event, false);
3069 if (_operation == StartTrim) {
3070 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3072 /* This must happen before the region's StatefulDiffCommand is created, as it may
3073 `correct' (ahem) the region's _start from being negative to being zero. It
3074 needs to be zero in the undo record.
3076 i->view->trim_front_ending ();
3078 if (_preserve_fade_anchor) {
3079 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3081 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3082 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3083 ar->set_fade_in_length(i->anchored_fade_length);
3084 ar->set_fade_in_active(true);
3087 if (_jump_position_when_done) {
3088 i->view->region()->set_position (i->initial_position);
3091 } else if (_operation == EndTrim) {
3092 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3093 if (_preserve_fade_anchor) {
3094 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3096 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3097 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3098 ar->set_fade_out_length(i->anchored_fade_length);
3099 ar->set_fade_out_active(true);
3102 if (_jump_position_when_done) {
3103 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3108 if (!_editor->selection->selected (_primary)) {
3109 _primary->thaw_after_trim ();
3112 set<boost::shared_ptr<Playlist> > diffed_playlists;
3114 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3115 i->view->thaw_after_trim ();
3116 i->view->enable_display (true);
3118 /* Trimming one region may affect others on the playlist, so we need
3119 to get undo Commands from the whole playlist rather than just the
3120 region. Use diffed_playlists to make sure we don't diff a given
3121 playlist more than once.
3123 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3124 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3125 vector<Command*> cmds;
3127 _editor->session()->add_commands (cmds);
3128 diffed_playlists.insert (p);
3133 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3137 _editor->motion_frozen_playlists.clear ();
3138 _editor->commit_reversible_command();
3141 /* no mouse movement */
3142 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false).frame) {
3143 _editor->point_trim (event, adjusted_current_frame (event));
3147 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3148 i->view->region()->resume_property_changes ();
3153 TrimDrag::aborted (bool movement_occurred)
3155 /* Our motion method is changing model state, so use the Undo system
3156 to cancel. Perhaps not ideal, as this will leave an Undo point
3157 behind which may be slightly odd from the user's point of view.
3161 finished (&ev, true);
3163 if (movement_occurred) {
3164 _editor->session()->undo (1);
3167 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3168 i->view->region()->resume_property_changes ();
3173 TrimDrag::setup_pointer_frame_offset ()
3175 list<DraggingView>::iterator i = _views.begin ();
3176 while (i != _views.end() && i->view != _primary) {
3180 if (i == _views.end()) {
3184 switch (_operation) {
3186 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3189 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3196 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3199 , _old_snap_type (e->snap_type())
3200 , _old_snap_mode (e->snap_mode())
3203 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3204 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3206 _real_section = &_marker->meter();
3211 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3213 Drag::start_grab (event, cursor);
3214 show_verbose_cursor_time (adjusted_current_frame(event));
3218 MeterMarkerDrag::setup_pointer_frame_offset ()
3220 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3224 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3227 // create a dummy marker to catch events, then hide it.
3230 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3232 _marker = new MeterMarker (
3234 *_editor->meter_group,
3235 UIConfiguration::instance().color ("meter marker"),
3237 *new MeterSection (_marker->meter())
3240 /* use the new marker for the grab */
3241 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3244 TempoMap& map (_editor->session()->tempo_map());
3245 /* get current state */
3246 before_state = &map.get_state();
3249 _editor->begin_reversible_command (_("move meter mark"));
3251 _editor->begin_reversible_command (_("copy meter mark"));
3253 Timecode::BBT_Time bbt = _real_section->bbt();
3255 /* we can't add a meter where one currently exists */
3256 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3261 const double beat = map.beat_at_bbt (bbt);
3262 const framepos_t frame = map.frame_at_beat (beat);
3263 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3264 , beat, bbt, frame, _real_section->position_lock_style());
3265 if (!_real_section) {
3271 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3272 if (_real_section->position_lock_style() != AudioTime) {
3273 _editor->set_snap_to (SnapToBar);
3274 _editor->set_snap_mode (SnapNormal);
3278 framepos_t pf = adjusted_current_frame (event);
3280 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3281 /* never snap to music for audio locked */
3282 pf = adjusted_current_frame (event, false);
3285 _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3287 /* fake marker meeds to stay under the mouse, unlike the real one. */
3288 _marker->set_position (adjusted_current_frame (event, false));
3290 show_verbose_cursor_time (_real_section->frame());
3294 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3296 if (!movement_occurred) {
3297 if (was_double_click()) {
3298 _editor->edit_meter_marker (*_marker);
3303 /* reinstate old snap setting */
3304 _editor->set_snap_to (_old_snap_type);
3305 _editor->set_snap_mode (_old_snap_mode);
3307 TempoMap& map (_editor->session()->tempo_map());
3309 XMLNode &after = map.get_state();
3310 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3311 _editor->commit_reversible_command ();
3313 // delete the dummy marker we used for visual representation while moving.
3314 // a new visual marker will show up automatically.
3319 MeterMarkerDrag::aborted (bool moved)
3321 _marker->set_position (_marker->meter().frame ());
3323 /* reinstate old snap setting */
3324 _editor->set_snap_to (_old_snap_type);
3325 _editor->set_snap_mode (_old_snap_mode);
3327 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3328 // delete the dummy marker we used for visual representation while moving.
3329 // a new visual marker will show up automatically.
3334 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3340 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3342 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3343 _real_section = &_marker->tempo();
3344 _movable = !_real_section->initial();
3345 _grab_bpm = _real_section->note_types_per_minute();
3350 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3352 Drag::start_grab (event, cursor);
3353 if (!_real_section->active()) {
3354 show_verbose_cursor_text (_("inactive"));
3356 show_verbose_cursor_time (adjusted_current_frame (event));
3361 TempoMarkerDrag::setup_pointer_frame_offset ()
3363 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3367 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3369 if (!_real_section->active()) {
3375 // mvc drag - create a dummy marker to catch events, hide it.
3378 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3380 TempoSection section (_marker->tempo());
3382 _marker = new TempoMarker (
3384 *_editor->tempo_group,
3385 UIConfiguration::instance().color ("tempo marker"),
3387 *new TempoSection (_marker->tempo())
3390 /* use the new marker for the grab */
3391 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3394 TempoMap& map (_editor->session()->tempo_map());
3395 /* get current state */
3396 before_state = &map.get_state();
3399 _editor->begin_reversible_command (_("move tempo mark"));
3402 const Tempo tempo (_marker->tempo());
3403 const framepos_t frame = adjusted_current_frame (event) + 1;
3404 const TempoSection::Type type = _real_section->type();
3406 _editor->begin_reversible_command (_("copy tempo mark"));
3408 if (_real_section->position_lock_style() == MusicTime) {
3409 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3410 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3412 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3415 if (!_real_section) {
3423 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3424 /* use vertical movement to alter tempo .. should be log */
3425 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3427 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3429 show_verbose_cursor_text (strs.str());
3431 } else if (_movable && !_real_section->locked_to_meter()) {
3434 if (_editor->snap_musical()) {
3435 /* we can't snap to a grid that we are about to move.
3436 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3438 pf = adjusted_current_frame (event, false);
3440 pf = adjusted_current_frame (event);
3443 TempoMap& map (_editor->session()->tempo_map());
3445 /* snap to beat is 1, snap to bar is -1 (sorry) */
3446 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3448 map.gui_set_tempo_position (_real_section, pf, sub_num);
3450 show_verbose_cursor_time (_real_section->frame());
3452 _marker->set_position (adjusted_current_frame (event, false));
3456 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3458 if (!_real_section->active()) {
3461 if (!movement_occurred) {
3462 if (was_double_click()) {
3463 _editor->edit_tempo_marker (*_marker);
3468 TempoMap& map (_editor->session()->tempo_map());
3470 XMLNode &after = map.get_state();
3471 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3472 _editor->commit_reversible_command ();
3474 // delete the dummy marker we used for visual representation while moving.
3475 // a new visual marker will show up automatically.
3480 TempoMarkerDrag::aborted (bool moved)
3482 _marker->set_position (_marker->tempo().frame());
3484 TempoMap& map (_editor->session()->tempo_map());
3485 map.set_state (*before_state, Stateful::current_state_version);
3486 // delete the dummy (hidden) marker we used for events while moving.
3491 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3497 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3502 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3504 Drag::start_grab (event, cursor);
3505 TempoMap& map (_editor->session()->tempo_map());
3506 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3509 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3510 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3511 show_verbose_cursor_text (sstr.str());
3512 finished (event, false);
3516 BBTRulerDrag::setup_pointer_frame_offset ()
3518 TempoMap& map (_editor->session()->tempo_map());
3519 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3520 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3523 if (divisions > 0) {
3524 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3526 /* while it makes some sense for the user to determine the division to 'grab',
3527 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3528 and the result over steep tempo curves. Use sixteenths.
3530 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3533 _grab_qn = map.quarter_note_at_beat (beat);
3535 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3540 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3542 TempoMap& map (_editor->session()->tempo_map());
3545 /* get current state */
3546 before_state = &map.get_state();
3547 _editor->begin_reversible_command (_("stretch tempo"));
3552 if (_editor->snap_musical()) {
3553 pf = adjusted_current_frame (event, false);
3555 pf = adjusted_current_frame (event);
3558 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3559 /* adjust previous tempo to match pointer frame */
3560 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3563 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3564 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3565 show_verbose_cursor_text (sstr.str());
3569 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3571 if (!movement_occurred) {
3575 TempoMap& map (_editor->session()->tempo_map());
3577 XMLNode &after = map.get_state();
3578 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3579 _editor->commit_reversible_command ();
3583 BBTRulerDrag::aborted (bool moved)
3586 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3591 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3592 : Drag (e, &c.track_canvas_item(), false)
3597 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3600 /** Do all the things we do when dragging the playhead to make it look as though
3601 * we have located, without actually doing the locate (because that would cause
3602 * the diskstream buffers to be refilled, which is too slow).
3605 CursorDrag::fake_locate (framepos_t t)
3607 if (_editor->session () == 0) {
3611 _editor->playhead_cursor->set_position (t);
3613 Session* s = _editor->session ();
3614 if (s->timecode_transmission_suspended ()) {
3615 framepos_t const f = _editor->playhead_cursor->current_frame ();
3616 /* This is asynchronous so it will be sent "now"
3618 s->send_mmc_locate (f);
3619 /* These are synchronous and will be sent during the next
3622 s->queue_full_time_code ();
3623 s->queue_song_position_pointer ();
3626 show_verbose_cursor_time (t);
3627 _editor->UpdateAllTransportClocks (t);
3631 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3633 Drag::start_grab (event, c);
3634 setup_snap_delta (_editor->playhead_cursor->current_frame());
3636 _grab_zoom = _editor->samples_per_pixel;
3638 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3640 _editor->snap_to_with_modifier (where, event);
3641 _editor->_dragging_playhead = true;
3643 Session* s = _editor->session ();
3645 /* grab the track canvas item as well */
3647 _cursor.track_canvas_item().grab();
3650 if (_was_rolling && _stop) {
3654 if (s->is_auditioning()) {
3655 s->cancel_audition ();
3659 if (AudioEngine::instance()->connected()) {
3661 /* do this only if we're the engine is connected
3662 * because otherwise this request will never be
3663 * serviced and we'll busy wait forever. likewise,
3664 * notice if we are disconnected while waiting for the
3665 * request to be serviced.
3668 s->request_suspend_timecode_transmission ();
3669 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3670 /* twiddle our thumbs */
3675 fake_locate (where.frame - snap_delta (event->button.state));
3679 CursorDrag::motion (GdkEvent* event, bool)
3681 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3683 _editor->snap_to_with_modifier (where, event);
3685 if (where.frame != last_pointer_frame()) {
3686 fake_locate (where.frame - snap_delta (event->button.state));
3691 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3693 _editor->_dragging_playhead = false;
3695 _cursor.track_canvas_item().ungrab();
3697 if (!movement_occurred && _stop) {
3701 motion (event, false);
3703 Session* s = _editor->session ();
3705 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3706 _editor->_pending_locate_request = true;
3707 s->request_resume_timecode_transmission ();
3712 CursorDrag::aborted (bool)
3714 _cursor.track_canvas_item().ungrab();
3716 if (_editor->_dragging_playhead) {
3717 _editor->session()->request_resume_timecode_transmission ();
3718 _editor->_dragging_playhead = false;
3721 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false).frame);
3724 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3725 : RegionDrag (e, i, p, v)
3727 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3731 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3733 Drag::start_grab (event, cursor);
3735 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3736 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3737 setup_snap_delta (r->position());
3739 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3743 FadeInDrag::setup_pointer_frame_offset ()
3745 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3746 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3747 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3751 FadeInDrag::motion (GdkEvent* event, bool)
3753 framecnt_t fade_length;
3755 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3756 _editor->snap_to_with_modifier (pos, event);
3758 pos.frame -= snap_delta (event->button.state);
3760 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3762 if (pos.frame < (region->position() + 64)) {
3763 fade_length = 64; // this should be a minimum defined somewhere
3764 } else if (pos.frame > region->position() + region->length() - region->fade_out()->back()->when) {
3765 fade_length = region->length() - region->fade_out()->back()->when - 1;
3767 fade_length = pos.frame - region->position();
3770 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3772 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3778 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3781 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3785 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3787 if (!movement_occurred) {
3791 framecnt_t fade_length;
3792 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3794 _editor->snap_to_with_modifier (pos, event);
3795 pos.frame -= snap_delta (event->button.state);
3797 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3799 if (pos.frame < (region->position() + 64)) {
3800 fade_length = 64; // this should be a minimum defined somewhere
3801 } else if (pos.frame >= region->position() + region->length() - region->fade_out()->back()->when) {
3802 fade_length = region->length() - region->fade_out()->back()->when - 1;
3804 fade_length = pos.frame - region->position();
3807 bool in_command = false;
3809 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3811 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3817 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3818 XMLNode &before = alist->get_state();
3820 tmp->audio_region()->set_fade_in_length (fade_length);
3821 tmp->audio_region()->set_fade_in_active (true);
3824 _editor->begin_reversible_command (_("change fade in length"));
3827 XMLNode &after = alist->get_state();
3828 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3832 _editor->commit_reversible_command ();
3837 FadeInDrag::aborted (bool)
3839 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3840 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3846 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3850 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3851 : RegionDrag (e, i, p, v)
3853 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3857 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3859 Drag::start_grab (event, cursor);
3861 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3862 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3863 setup_snap_delta (r->last_frame());
3865 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3869 FadeOutDrag::setup_pointer_frame_offset ()
3871 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3872 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3873 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3877 FadeOutDrag::motion (GdkEvent* event, bool)
3879 framecnt_t fade_length;
3880 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3882 _editor->snap_to_with_modifier (pos, event);
3883 pos.frame -= snap_delta (event->button.state);
3885 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3887 if (pos.frame > (region->last_frame() - 64)) {
3888 fade_length = 64; // this should really be a minimum fade defined somewhere
3889 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
3890 fade_length = region->length() - region->fade_in()->back()->when - 1;
3892 fade_length = region->last_frame() - pos.frame;
3895 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3897 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3903 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3906 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3910 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3912 if (!movement_occurred) {
3916 framecnt_t fade_length;
3917 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3919 _editor->snap_to_with_modifier (pos, event);
3920 pos.frame -= snap_delta (event->button.state);
3922 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3924 if (pos.frame > (region->last_frame() - 64)) {
3925 fade_length = 64; // this should really be a minimum fade defined somewhere
3926 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
3927 fade_length = region->length() - region->fade_in()->back()->when - 1;
3929 fade_length = region->last_frame() - pos.frame;
3932 bool in_command = false;
3934 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3936 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3942 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3943 XMLNode &before = alist->get_state();
3945 tmp->audio_region()->set_fade_out_length (fade_length);
3946 tmp->audio_region()->set_fade_out_active (true);
3949 _editor->begin_reversible_command (_("change fade out length"));
3952 XMLNode &after = alist->get_state();
3953 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3957 _editor->commit_reversible_command ();
3962 FadeOutDrag::aborted (bool)
3964 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3965 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3971 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3975 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3977 , _selection_changed (false)
3979 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3980 Gtk::Window* toplevel = _editor->current_toplevel();
3981 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3985 _points.push_back (ArdourCanvas::Duple (0, 0));
3987 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3990 MarkerDrag::~MarkerDrag ()
3992 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3997 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3999 location = new Location (*l);
4000 markers.push_back (m);
4005 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4007 Drag::start_grab (event, cursor);
4011 Location *location = _editor->find_location_from_marker (_marker, is_start);
4012 _editor->_dragging_edit_point = true;
4014 update_item (location);
4016 // _drag_line->show();
4017 // _line->raise_to_top();
4020 show_verbose_cursor_time (location->start());
4022 show_verbose_cursor_time (location->end());
4024 setup_snap_delta (is_start ? location->start() : location->end());
4026 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4029 case Selection::Toggle:
4030 /* we toggle on the button release */
4032 case Selection::Set:
4033 if (!_editor->selection->selected (_marker)) {
4034 _editor->selection->set (_marker);
4035 _selection_changed = true;
4038 case Selection::Extend:
4040 Locations::LocationList ll;
4041 list<ArdourMarker*> to_add;
4043 _editor->selection->markers.range (s, e);
4044 s = min (_marker->position(), s);
4045 e = max (_marker->position(), e);
4048 if (e < max_framepos) {
4051 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4052 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4053 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4056 to_add.push_back (lm->start);
4059 to_add.push_back (lm->end);
4063 if (!to_add.empty()) {
4064 _editor->selection->add (to_add);
4065 _selection_changed = true;
4069 case Selection::Add:
4070 _editor->selection->add (_marker);
4071 _selection_changed = true;
4076 /* Set up copies for us to manipulate during the drag
4079 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4081 Location* l = _editor->find_location_from_marker (*i, is_start);
4088 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4090 /* range: check that the other end of the range isn't
4093 CopiedLocationInfo::iterator x;
4094 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4095 if (*(*x).location == *l) {
4099 if (x == _copied_locations.end()) {
4100 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4102 (*x).markers.push_back (*i);
4103 (*x).move_both = true;
4111 MarkerDrag::setup_pointer_frame_offset ()
4114 Location *location = _editor->find_location_from_marker (_marker, is_start);
4115 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4119 MarkerDrag::motion (GdkEvent* event, bool)
4121 framecnt_t f_delta = 0;
4123 bool move_both = false;
4124 Location *real_location;
4125 Location *copy_location = 0;
4126 framecnt_t const sd = snap_delta (event->button.state);
4128 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true).frame - sd;
4129 framepos_t next = newframe;
4131 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4135 CopiedLocationInfo::iterator x;
4137 /* find the marker we're dragging, and compute the delta */
4139 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4141 copy_location = (*x).location;
4143 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4145 /* this marker is represented by this
4146 * CopiedLocationMarkerInfo
4149 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4154 if (real_location->is_mark()) {
4155 f_delta = newframe - copy_location->start();
4159 switch (_marker->type()) {
4160 case ArdourMarker::SessionStart:
4161 case ArdourMarker::RangeStart:
4162 case ArdourMarker::LoopStart:
4163 case ArdourMarker::PunchIn:
4164 f_delta = newframe - copy_location->start();
4167 case ArdourMarker::SessionEnd:
4168 case ArdourMarker::RangeEnd:
4169 case ArdourMarker::LoopEnd:
4170 case ArdourMarker::PunchOut:
4171 f_delta = newframe - copy_location->end();
4174 /* what kind of marker is this ? */
4183 if (x == _copied_locations.end()) {
4184 /* hmm, impossible - we didn't find the dragged marker */
4188 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4190 /* now move them all */
4192 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4194 copy_location = x->location;
4196 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4200 if (real_location->locked()) {
4204 if (copy_location->is_mark()) {
4207 copy_location->set_start (copy_location->start() + f_delta, false, true, divisions);
4211 framepos_t new_start = copy_location->start() + f_delta;
4212 framepos_t new_end = copy_location->end() + f_delta;
4214 if (is_start) { // start-of-range marker
4216 if (move_both || (*x).move_both) {
4217 copy_location->set_start (new_start, false, true, divisions);
4218 copy_location->set_end (new_end, false, true, divisions);
4219 } else if (new_start < copy_location->end()) {
4220 copy_location->set_start (new_start, false, true, divisions);
4221 } else if (newframe > 0) {
4222 //_editor->snap_to (next, RoundUpAlways, true);
4223 copy_location->set_end (next, false, true, divisions);
4224 copy_location->set_start (newframe, false, true, divisions);
4227 } else { // end marker
4229 if (move_both || (*x).move_both) {
4230 copy_location->set_end (new_end, divisions);
4231 copy_location->set_start (new_start, false, true, divisions);
4232 } else if (new_end > copy_location->start()) {
4233 copy_location->set_end (new_end, false, true, divisions);
4234 } else if (newframe > 0) {
4235 //_editor->snap_to (next, RoundDownAlways, true);
4236 copy_location->set_start (next, false, true, divisions);
4237 copy_location->set_end (newframe, false, true, divisions);
4242 update_item (copy_location);
4244 /* now lookup the actual GUI items used to display this
4245 * location and move them to wherever the copy of the location
4246 * is now. This means that the logic in ARDOUR::Location is
4247 * still enforced, even though we are not (yet) modifying
4248 * the real Location itself.
4251 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4254 lm->set_position (copy_location->start(), copy_location->end());
4259 assert (!_copied_locations.empty());
4261 show_verbose_cursor_time (newframe);
4265 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4267 if (!movement_occurred) {
4269 if (was_double_click()) {
4270 _editor->rename_marker (_marker);
4274 /* just a click, do nothing but finish
4275 off the selection process
4278 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4280 case Selection::Set:
4281 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4282 _editor->selection->set (_marker);
4283 _selection_changed = true;
4287 case Selection::Toggle:
4288 /* we toggle on the button release, click only */
4289 _editor->selection->toggle (_marker);
4290 _selection_changed = true;
4294 case Selection::Extend:
4295 case Selection::Add:
4299 if (_selection_changed) {
4300 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4301 _editor->commit_reversible_selection_op();
4307 _editor->_dragging_edit_point = false;
4309 XMLNode &before = _editor->session()->locations()->get_state();
4310 bool in_command = false;
4312 MarkerSelection::iterator i;
4313 CopiedLocationInfo::iterator x;
4314 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4317 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4318 x != _copied_locations.end() && i != _editor->selection->markers.end();
4321 Location * location = _editor->find_location_from_marker (*i, is_start);
4325 if (location->locked()) {
4329 _editor->begin_reversible_command ( _("move marker") );
4332 if (location->is_mark()) {
4333 location->set_start (((*x).location)->start(), false, true, divisions);
4335 location->set (((*x).location)->start(), ((*x).location)->end(), true, divisions);
4338 if (location->is_session_range()) {
4339 _editor->session()->set_end_is_free (false);
4345 XMLNode &after = _editor->session()->locations()->get_state();
4346 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4347 _editor->commit_reversible_command ();
4352 MarkerDrag::aborted (bool movement_occurred)
4354 if (!movement_occurred) {
4358 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4360 /* move all markers to their original location */
4363 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4366 Location * location = _editor->find_location_from_marker (*m, is_start);
4369 (*m)->set_position (is_start ? location->start() : location->end());
4376 MarkerDrag::update_item (Location*)
4381 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4383 , _fixed_grab_x (0.0)
4384 , _fixed_grab_y (0.0)
4385 , _cumulative_x_drag (0.0)
4386 , _cumulative_y_drag (0.0)
4390 if (_zero_gain_fraction < 0.0) {
4391 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4394 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4396 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4402 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4404 Drag::start_grab (event, _editor->cursors()->fader);
4406 // start the grab at the center of the control point so
4407 // the point doesn't 'jump' to the mouse after the first drag
4408 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4409 _fixed_grab_y = _point->get_y();
4411 setup_snap_delta (_editor->pixel_to_sample (_fixed_grab_x));
4413 float const fraction = 1 - (_point->get_y() / _point->line().height());
4414 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4416 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4418 if (!_point->can_slide ()) {
4419 _x_constrained = true;
4424 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4426 double dx = _drags->current_pointer_x() - last_pointer_x();
4427 double dy = current_pointer_y() - last_pointer_y();
4428 bool need_snap = true;
4430 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4436 /* coordinate in pixels relative to the start of the region (for region-based automation)
4437 or track (for track-based automation) */
4438 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4439 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4441 // calculate zero crossing point. back off by .01 to stay on the
4442 // positive side of zero
4443 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4445 if (_x_constrained) {
4448 if (_y_constrained) {
4452 _cumulative_x_drag = cx - _fixed_grab_x;
4453 _cumulative_y_drag = cy - _fixed_grab_y;
4457 cy = min ((double) _point->line().height(), cy);
4459 // make sure we hit zero when passing through
4460 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4464 MusicFrame cx_mf (_editor->pixel_to_sample (cx) + snap_delta (event->button.state), 0);
4466 if (!_x_constrained && need_snap) {
4467 _editor->snap_to_with_modifier (cx_mf, event);
4470 cx_mf.frame -= snap_delta (event->button.state);
4471 cx_mf.frame = min (cx_mf.frame, _point->line().maximum_time() + _point->line().offset());
4473 float const fraction = 1.0 - (cy / _point->line().height());
4476 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4477 _editor->begin_reversible_command (_("automation event move"));
4478 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4480 pair<double, float> result;
4481 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
4483 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4487 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4489 if (!movement_occurred) {
4492 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4493 _editor->reset_point_selection ();
4497 _point->line().end_drag (_pushing, _final_index);
4498 _editor->commit_reversible_command ();
4503 ControlPointDrag::aborted (bool)
4505 _point->line().reset ();
4509 ControlPointDrag::active (Editing::MouseMode m)
4511 if (m == Editing::MouseDraw) {
4512 /* always active in mouse draw */
4516 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4517 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4520 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4523 , _fixed_grab_x (0.0)
4524 , _fixed_grab_y (0.0)
4525 , _cumulative_y_drag (0)
4529 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4533 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4535 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4538 _item = &_line->grab_item ();
4540 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4541 origin, and ditto for y.
4544 double mx = event->button.x;
4545 double my = event->button.y;
4547 _line->grab_item().canvas_to_item (mx, my);
4549 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4551 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4552 /* no adjacent points */
4556 Drag::start_grab (event, _editor->cursors()->fader);
4558 /* store grab start in item frame */
4559 double const bx = _line->nth (_before)->get_x();
4560 double const ax = _line->nth (_after)->get_x();
4561 double const click_ratio = (ax - mx) / (ax - bx);
4563 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4568 double fraction = 1.0 - (cy / _line->height());
4570 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4574 LineDrag::motion (GdkEvent* event, bool first_move)
4576 double dy = current_pointer_y() - last_pointer_y();
4578 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4582 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4584 _cumulative_y_drag = cy - _fixed_grab_y;
4587 cy = min ((double) _line->height(), cy);
4589 double const fraction = 1.0 - (cy / _line->height());
4593 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4595 _editor->begin_reversible_command (_("automation range move"));
4596 _line->start_drag_line (_before, _after, initial_fraction);
4599 /* we are ignoring x position for this drag, so we can just pass in anything */
4600 pair<double, float> result;
4602 result = _line->drag_motion (0, fraction, true, false, ignored);
4603 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4607 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4609 if (movement_occurred) {
4610 motion (event, false);
4611 _line->end_drag (false, 0);
4612 _editor->commit_reversible_command ();
4614 /* add a new control point on the line */
4616 AutomationTimeAxisView* atv;
4618 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4619 framepos_t where = grab_frame ();
4622 double cy = _fixed_grab_y;
4624 _line->grab_item().item_to_canvas (cx, cy);
4626 atv->add_automation_event (event, where, cy, false);
4627 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4628 AudioRegionView* arv;
4630 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4631 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4638 LineDrag::aborted (bool)
4643 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4647 _region_view_grab_x (0.0),
4648 _cumulative_x_drag (0),
4652 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4656 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4658 Drag::start_grab (event);
4660 _line = reinterpret_cast<Line*> (_item);
4663 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4665 double cx = event->button.x;
4666 double cy = event->button.y;
4668 _item->parent()->canvas_to_item (cx, cy);
4670 /* store grab start in parent frame */
4671 _region_view_grab_x = cx;
4673 _before = *(float*) _item->get_data ("position");
4675 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4677 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4681 FeatureLineDrag::motion (GdkEvent*, bool)
4683 double dx = _drags->current_pointer_x() - last_pointer_x();
4685 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4687 _cumulative_x_drag += dx;
4689 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4698 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4700 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4702 float *pos = new float;
4705 _line->set_data ("position", pos);
4711 FeatureLineDrag::finished (GdkEvent*, bool)
4713 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4714 _arv->update_transient(_before, _before);
4718 FeatureLineDrag::aborted (bool)
4723 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4725 , _vertical_only (false)
4727 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4731 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4733 Drag::start_grab (event);
4734 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4738 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4744 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4745 MusicFrame grab (grab_frame (), 0);
4747 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4748 _editor->snap_to_with_modifier (grab, event);
4750 grab.frame = raw_grab_frame ();
4753 /* base start and end on initial click position */
4755 if (pf < grab.frame) {
4763 if (current_pointer_y() < grab_y()) {
4764 y1 = current_pointer_y();
4767 y2 = current_pointer_y();
4771 if (start != end || y1 != y2) {
4773 double x1 = _editor->sample_to_pixel (start);
4774 double x2 = _editor->sample_to_pixel (end);
4775 const double min_dimension = 2.0;
4777 if (_vertical_only) {
4778 /* fixed 10 pixel width */
4782 x2 = min (x1 - min_dimension, x2);
4784 x2 = max (x1 + min_dimension, x2);
4789 y2 = min (y1 - min_dimension, y2);
4791 y2 = max (y1 + min_dimension, y2);
4794 /* translate rect into item space and set */
4796 ArdourCanvas::Rect r (x1, y1, x2, y2);
4798 /* this drag is a _trackview_only == true drag, so the y1 and
4799 * y2 (computed using current_pointer_y() and grab_y()) will be
4800 * relative to the top of the trackview group). The
4801 * rubberband rect has the same parent/scroll offset as the
4802 * the trackview group, so we can use the "r" rect directly
4803 * to set the shape of the rubberband.
4806 _editor->rubberband_rect->set (r);
4807 _editor->rubberband_rect->show();
4808 _editor->rubberband_rect->raise_to_top();
4810 show_verbose_cursor_time (pf);
4812 do_select_things (event, true);
4817 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4821 framepos_t grab = grab_frame ();
4822 framepos_t lpf = last_pointer_frame ();
4824 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4825 grab = raw_grab_frame ();
4826 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4840 if (current_pointer_y() < grab_y()) {
4841 y1 = current_pointer_y();
4844 y2 = current_pointer_y();
4848 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4852 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4854 if (movement_occurred) {
4856 motion (event, false);
4857 do_select_things (event, false);
4863 bool do_deselect = true;
4864 MidiTimeAxisView* mtv;
4866 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4868 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4869 /* nothing selected */
4870 add_midi_region (mtv, true);
4871 do_deselect = false;
4875 /* do not deselect if Primary or Tertiary (toggle-select or
4876 * extend-select are pressed.
4879 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4880 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4887 _editor->rubberband_rect->hide();
4891 RubberbandSelectDrag::aborted (bool)
4893 _editor->rubberband_rect->hide ();
4896 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4897 : RegionDrag (e, i, p, v)
4899 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4903 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4905 Drag::start_grab (event, cursor);
4907 _editor->get_selection().add (_primary);
4909 framepos_t where = _primary->region()->position();
4910 setup_snap_delta (where);
4912 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4916 TimeFXDrag::motion (GdkEvent* event, bool)
4918 RegionView* rv = _primary;
4919 StreamView* cv = rv->get_time_axis_view().view ();
4920 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4921 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4922 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4923 MusicFrame pf (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4925 _editor->snap_to_with_modifier (pf, event);
4926 pf.frame -= snap_delta (event->button.state);
4928 if (pf.frame > rv->region()->position()) {
4929 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf.frame, layers, layer);
4932 show_verbose_cursor_duration (_primary->region()->position(), pf.frame, 0);
4936 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4938 /* this may have been a single click, no drag. We still want the dialog
4939 to show up in that case, so that the user can manually edit the
4940 parameters for the timestretch.
4943 float fraction = 1.0;
4945 if (movement_occurred) {
4947 motion (event, false);
4949 _primary->get_time_axis_view().hide_timestretch ();
4951 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4953 if (adjusted_frame_pos < _primary->region()->position()) {
4954 /* backwards drag of the left edge - not usable */
4958 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4960 fraction = (double) newlen / (double) _primary->region()->length();
4962 #ifndef USE_RUBBERBAND
4963 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4964 if (_primary->region()->data_type() == DataType::AUDIO) {
4965 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4970 if (!_editor->get_selection().regions.empty()) {
4971 /* primary will already be included in the selection, and edit
4972 group shared editing will propagate selection across
4973 equivalent regions, so just use the current region
4977 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4978 error << _("An error occurred while executing time stretch operation") << endmsg;
4984 TimeFXDrag::aborted (bool)
4986 _primary->get_time_axis_view().hide_timestretch ();
4989 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4992 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4996 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4998 Drag::start_grab (event);
5002 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5004 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5008 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5010 if (movement_occurred && _editor->session()) {
5011 /* make sure we stop */
5012 _editor->session()->request_transport_speed (0.0);
5017 ScrubDrag::aborted (bool)
5022 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5026 , _track_selection_at_start (e)
5027 , _time_selection_at_start (!_editor->get_selection().time.empty())
5029 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5031 if (_time_selection_at_start) {
5032 start_at_start = _editor->get_selection().time.start();
5033 end_at_start = _editor->get_selection().time.end_frame();
5038 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5040 if (_editor->session() == 0) {
5044 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5046 switch (_operation) {
5047 case CreateSelection:
5048 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5053 cursor = _editor->cursors()->selector;
5054 Drag::start_grab (event, cursor);
5057 case SelectionStartTrim:
5058 if (_editor->clicked_axisview) {
5059 _editor->clicked_axisview->order_selection_trims (_item, true);
5061 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5064 case SelectionEndTrim:
5065 if (_editor->clicked_axisview) {
5066 _editor->clicked_axisview->order_selection_trims (_item, false);
5068 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5072 Drag::start_grab (event, cursor);
5075 case SelectionExtend:
5076 Drag::start_grab (event, cursor);
5080 if (_operation == SelectionMove) {
5081 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5083 show_verbose_cursor_time (adjusted_current_frame (event));
5088 SelectionDrag::setup_pointer_frame_offset ()
5090 switch (_operation) {
5091 case CreateSelection:
5092 _pointer_frame_offset = 0;
5095 case SelectionStartTrim:
5097 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5100 case SelectionEndTrim:
5101 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5104 case SelectionExtend:
5110 SelectionDrag::motion (GdkEvent* event, bool first_move)
5112 framepos_t start = 0;
5114 framecnt_t length = 0;
5115 framecnt_t distance = 0;
5116 MusicFrame start_mf (0, 0);
5117 framepos_t const pending_position = adjusted_current_frame (event);
5119 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5124 _track_selection_at_start = _editor->selection->tracks;
5127 switch (_operation) {
5128 case CreateSelection:
5130 MusicFrame grab (grab_frame (), 0);
5132 grab.frame = adjusted_current_frame (event, false);
5133 if (grab.frame < pending_position) {
5134 _editor->snap_to (grab, RoundDownMaybe);
5136 _editor->snap_to (grab, RoundUpMaybe);
5140 if (pending_position < grab.frame) {
5141 start = pending_position;
5144 end = pending_position;
5148 /* first drag: Either add to the selection
5149 or create a new selection
5156 /* adding to the selection */
5157 _editor->set_selected_track_as_side_effect (Selection::Add);
5158 _editor->clicked_selection = _editor->selection->add (start, end);
5165 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5166 _editor->set_selected_track_as_side_effect (Selection::Set);
5169 _editor->clicked_selection = _editor->selection->set (start, end);
5173 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5174 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5175 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5177 _editor->selection->add (atest);
5181 /* select all tracks within the rectangle that we've marked out so far */
5182 TrackViewList new_selection;
5183 TrackViewList& all_tracks (_editor->track_views);
5185 ArdourCanvas::Coord const top = grab_y();
5186 ArdourCanvas::Coord const bottom = current_pointer_y();
5188 if (top >= 0 && bottom >= 0) {
5190 //first, find the tracks that are covered in the y range selection
5191 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5192 if ((*i)->covered_by_y_range (top, bottom)) {
5193 new_selection.push_back (*i);
5197 //now compare our list with the current selection, and add as necessary
5198 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5199 TrackViewList tracks_to_add;
5200 TrackViewList tracks_to_remove;
5203 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
5204 if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
5205 tracks_to_remove.push_back (*i);
5210 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5211 if (!_editor->selection->tracks.contains (*i)) {
5212 tracks_to_add.push_back (*i);
5216 _editor->selection->add (tracks_to_add);
5218 if (!tracks_to_remove.empty()) {
5219 _editor->selection->remove (tracks_to_remove);
5225 case SelectionStartTrim:
5227 end = _editor->selection->time[_editor->clicked_selection].end;
5229 if (pending_position > end) {
5232 start = pending_position;
5236 case SelectionEndTrim:
5238 start = _editor->selection->time[_editor->clicked_selection].start;
5240 if (pending_position < start) {
5243 end = pending_position;
5250 start = _editor->selection->time[_editor->clicked_selection].start;
5251 end = _editor->selection->time[_editor->clicked_selection].end;
5253 length = end - start;
5254 distance = pending_position - start;
5255 start = pending_position;
5257 start_mf.frame = start;
5258 _editor->snap_to (start_mf);
5260 end = start_mf.frame + length;
5264 case SelectionExtend:
5269 switch (_operation) {
5271 if (_time_selection_at_start) {
5272 _editor->selection->move_time (distance);
5276 _editor->selection->replace (_editor->clicked_selection, start, end);
5280 if (_operation == SelectionMove) {
5281 show_verbose_cursor_time(start);
5283 show_verbose_cursor_time(pending_position);
5288 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5290 Session* s = _editor->session();
5292 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5293 if (movement_occurred) {
5294 motion (event, false);
5295 /* XXX this is not object-oriented programming at all. ick */
5296 if (_editor->selection->time.consolidate()) {
5297 _editor->selection->TimeChanged ();
5300 /* XXX what if its a music time selection? */
5302 if (s->get_play_range() && s->transport_rolling()) {
5303 s->request_play_range (&_editor->selection->time, true);
5304 } else if (!s->config.get_external_sync()) {
5305 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5306 s->request_locate (_editor->get_selection().time.start());
5310 if (_editor->get_selection().time.length() != 0) {
5311 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5313 s->clear_range_selection ();
5318 /* just a click, no pointer movement.
5321 if (was_double_click()) {
5322 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5323 _editor->temporal_zoom_selection (Both);
5328 if (_operation == SelectionExtend) {
5329 if (_time_selection_at_start) {
5330 framepos_t pos = adjusted_current_frame (event, false);
5331 framepos_t start = min (pos, start_at_start);
5332 framepos_t end = max (pos, end_at_start);
5333 _editor->selection->set (start, end);
5336 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5337 if (_editor->clicked_selection) {
5338 _editor->selection->remove (_editor->clicked_selection);
5341 if (!_editor->clicked_selection) {
5342 _editor->selection->clear_time();
5347 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5348 _editor->selection->set (_editor->clicked_axisview);
5351 if (s && s->get_play_range () && s->transport_rolling()) {
5352 s->request_stop (false, false);
5357 _editor->stop_canvas_autoscroll ();
5358 _editor->clicked_selection = 0;
5359 _editor->commit_reversible_selection_op ();
5363 SelectionDrag::aborted (bool)
5368 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5369 : Drag (e, i, false),
5373 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5375 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5376 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5377 physical_screen_height (_editor->current_toplevel()->get_window())));
5378 _drag_rect->hide ();
5380 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5381 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5384 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5386 /* normal canvas items will be cleaned up when their parent group is deleted. But
5387 this item is created as the child of a long-lived parent group, and so we
5388 need to explicitly delete it.
5394 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5396 if (_editor->session() == 0) {
5400 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5402 if (!_editor->temp_location) {
5403 _editor->temp_location = new Location (*_editor->session());
5406 switch (_operation) {
5407 case CreateSkipMarker:
5408 case CreateRangeMarker:
5409 case CreateTransportMarker:
5410 case CreateCDMarker:
5412 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5417 cursor = _editor->cursors()->selector;
5421 Drag::start_grab (event, cursor);
5423 show_verbose_cursor_time (adjusted_current_frame (event));
5427 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5429 framepos_t start = 0;
5431 ArdourCanvas::Rectangle *crect;
5433 switch (_operation) {
5434 case CreateSkipMarker:
5435 crect = _editor->range_bar_drag_rect;
5437 case CreateRangeMarker:
5438 crect = _editor->range_bar_drag_rect;
5440 case CreateTransportMarker:
5441 crect = _editor->transport_bar_drag_rect;
5443 case CreateCDMarker:
5444 crect = _editor->cd_marker_bar_drag_rect;
5447 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5452 framepos_t const pf = adjusted_current_frame (event);
5454 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5455 MusicFrame grab (grab_frame (), 0);
5456 _editor->snap_to (grab);
5458 if (pf < grab_frame()) {
5466 /* first drag: Either add to the selection
5467 or create a new selection.
5472 _editor->temp_location->set (start, end);
5476 update_item (_editor->temp_location);
5478 //_drag_rect->raise_to_top();
5484 _editor->temp_location->set (start, end);
5486 double x1 = _editor->sample_to_pixel (start);
5487 double x2 = _editor->sample_to_pixel (end);
5491 update_item (_editor->temp_location);
5494 show_verbose_cursor_time (pf);
5499 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5501 Location * newloc = 0;
5505 if (movement_occurred) {
5506 motion (event, false);
5509 switch (_operation) {
5510 case CreateSkipMarker:
5511 case CreateRangeMarker:
5512 case CreateCDMarker:
5514 XMLNode &before = _editor->session()->locations()->get_state();
5515 if (_operation == CreateSkipMarker) {
5516 _editor->begin_reversible_command (_("new skip marker"));
5517 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5518 flags = Location::IsRangeMarker | Location::IsSkip;
5519 _editor->range_bar_drag_rect->hide();
5520 } else if (_operation == CreateCDMarker) {
5521 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5522 _editor->begin_reversible_command (_("new CD marker"));
5523 flags = Location::IsRangeMarker | Location::IsCDMarker;
5524 _editor->cd_marker_bar_drag_rect->hide();
5526 _editor->begin_reversible_command (_("new skip marker"));
5527 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5528 flags = Location::IsRangeMarker;
5529 _editor->range_bar_drag_rect->hide();
5531 newloc = new Location (
5532 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5533 , _editor->get_grid_music_divisions (event->button.state));
5535 _editor->session()->locations()->add (newloc, true);
5536 XMLNode &after = _editor->session()->locations()->get_state();
5537 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5538 _editor->commit_reversible_command ();
5542 case CreateTransportMarker:
5543 // popup menu to pick loop or punch
5544 _editor->new_transport_marker_context_menu (&event->button, _item);
5550 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5552 if (_operation == CreateTransportMarker) {
5554 /* didn't drag, so just locate */
5556 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5558 } else if (_operation == CreateCDMarker) {
5560 /* didn't drag, but mark is already created so do
5563 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5568 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5570 if (end == max_framepos) {
5571 end = _editor->session()->current_end_frame ();
5574 if (start == max_framepos) {
5575 start = _editor->session()->current_start_frame ();
5578 switch (_editor->mouse_mode) {
5580 /* find the two markers on either side and then make the selection from it */
5581 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5585 /* find the two markers on either side of the click and make the range out of it */
5586 _editor->selection->set (start, end);
5595 _editor->stop_canvas_autoscroll ();
5599 RangeMarkerBarDrag::aborted (bool movement_occurred)
5601 if (movement_occurred) {
5602 _drag_rect->hide ();
5607 RangeMarkerBarDrag::update_item (Location* location)
5609 double const x1 = _editor->sample_to_pixel (location->start());
5610 double const x2 = _editor->sample_to_pixel (location->end());
5612 _drag_rect->set_x0 (x1);
5613 _drag_rect->set_x1 (x2);
5616 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5618 , _cumulative_dx (0)
5619 , _cumulative_dy (0)
5620 , _was_selected (false)
5623 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5625 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5627 _region = &_primary->region_view ();
5628 _note_height = _region->midi_stream_view()->note_height ();
5632 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5634 Drag::start_grab (event);
5636 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5642 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5644 if (!(_was_selected = _primary->selected())) {
5646 /* tertiary-click means extend selection - we'll do that on button release,
5647 so don't add it here, because otherwise we make it hard to figure
5648 out the "extend-to" range.
5651 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5654 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5657 _region->note_selected (_primary, true);
5659 _editor->get_selection().clear_points();
5660 _region->unique_select (_primary);
5666 /** @return Current total drag x change in frames */
5668 NoteDrag::total_dx (const guint state) const
5670 if (_x_constrained) {
5673 TempoMap& map (_editor->session()->tempo_map());
5676 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5678 /* primary note time */
5679 double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
5680 frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5682 /* new time of the primary note in session frames */
5683 frameoffset_t st = n + dx + snap_delta (state);
5685 framepos_t const rp = _region->region()->position ();
5687 /* prevent the note being dragged earlier than the region's position */
5690 /* possibly snap and return corresponding delta */
5694 if (ArdourKeyboard::indicates_snap (state)) {
5695 if (_editor->snap_mode () != SnapOff) {
5699 if (_editor->snap_mode () == SnapOff) {
5701 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5702 if (ArdourKeyboard::indicates_snap_delta (state)) {
5710 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5711 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5713 ret = st - n - snap_delta (state);
5718 /** @return Current total drag y change in note number */
5720 NoteDrag::total_dy () const
5722 if (_y_constrained) {
5726 double const y = _region->midi_view()->y_position ();
5727 /* new current note */
5728 uint8_t n = _region->y_to_note (current_pointer_y () - y);
5730 MidiStreamView* msv = _region->midi_stream_view ();
5731 n = max (msv->lowest_note(), n);
5732 n = min (msv->highest_note(), n);
5733 /* and work out delta */
5734 return n - _region->y_to_note (grab_y() - y);
5738 NoteDrag::motion (GdkEvent * event, bool first_move)
5740 if (_copy && first_move) {
5741 /* make copies of all the selected notes */
5742 _primary = _region->copy_selection ();
5745 /* Total change in x and y since the start of the drag */
5746 frameoffset_t const dx = total_dx (event->button.state);
5747 int8_t const dy = total_dy ();
5749 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5750 double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5751 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5754 _cumulative_dx += tdx;
5755 _cumulative_dy += tdy;
5757 int8_t note_delta = total_dy();
5761 _region->move_copies (tdx, tdy, note_delta);
5763 _region->move_selection (tdx, tdy, note_delta);
5766 /* the new note value may be the same as the old one, but we
5767 * don't know what that means because the selection may have
5768 * involved more than one note and we might be doing something
5769 * odd with them. so show the note value anyway, always.
5772 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5774 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5780 NoteDrag::finished (GdkEvent* ev, bool moved)
5783 /* no motion - select note */
5785 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5786 _editor->current_mouse_mode() == Editing::MouseDraw) {
5788 bool changed = false;
5790 if (_was_selected) {
5791 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5793 _region->note_deselected (_primary);
5796 _editor->get_selection().clear_points();
5797 _region->unique_select (_primary);
5801 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5802 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5804 if (!extend && !add && _region->selection_size() > 1) {
5805 _editor->get_selection().clear_points();
5806 _region->unique_select (_primary);
5808 } else if (extend) {
5809 _region->note_selected (_primary, true, true);
5812 /* it was added during button press */
5819 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5820 _editor->commit_reversible_selection_op();
5824 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy(), _copy);
5829 NoteDrag::aborted (bool)
5834 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5835 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5836 : Drag (editor, atv->base_item ())
5838 , _y_origin (atv->y_position())
5839 , _nothing_to_drag (false)
5841 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5842 setup (atv->lines ());
5845 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5846 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5847 : Drag (editor, rv->get_canvas_group ())
5849 , _y_origin (rv->get_time_axis_view().y_position())
5850 , _nothing_to_drag (false)
5853 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5855 list<boost::shared_ptr<AutomationLine> > lines;
5857 AudioRegionView* audio_view;
5858 AutomationRegionView* automation_view;
5859 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5860 lines.push_back (audio_view->get_gain_line ());
5861 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5862 lines.push_back (automation_view->line ());
5865 error << _("Automation range drag created for invalid region type") << endmsg;
5871 /** @param lines AutomationLines to drag.
5872 * @param offset Offset from the session start to the points in the AutomationLines.
5875 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5877 /* find the lines that overlap the ranges being dragged */
5878 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5879 while (i != lines.end ()) {
5880 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5883 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5885 /* check this range against all the AudioRanges that we are using */
5886 list<AudioRange>::const_iterator k = _ranges.begin ();
5887 while (k != _ranges.end()) {
5888 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5894 /* add it to our list if it overlaps at all */
5895 if (k != _ranges.end()) {
5900 _lines.push_back (n);
5906 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5910 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5912 return 1.0 - ((global_y - _y_origin) / line->height());
5916 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5918 const double v = list->eval(x);
5919 return _integral ? rint(v) : v;
5923 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5925 Drag::start_grab (event, cursor);
5927 /* Get line states before we start changing things */
5928 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5929 i->state = &i->line->get_state ();
5930 i->original_fraction = y_fraction (i->line, current_pointer_y());
5933 if (_ranges.empty()) {
5935 /* No selected time ranges: drag all points */
5936 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5937 uint32_t const N = i->line->npoints ();
5938 for (uint32_t j = 0; j < N; ++j) {
5939 i->points.push_back (i->line->nth (j));
5945 if (_nothing_to_drag) {
5951 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5953 if (_nothing_to_drag && !first_move) {
5958 _editor->begin_reversible_command (_("automation range move"));
5960 if (!_ranges.empty()) {
5962 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5964 framecnt_t const half = (i->start + i->end) / 2;
5966 /* find the line that this audio range starts in */
5967 list<Line>::iterator j = _lines.begin();
5968 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5972 if (j != _lines.end()) {
5973 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5975 /* j is the line that this audio range starts in; fade into it;
5976 64 samples length plucked out of thin air.
5979 framepos_t a = i->start + 64;
5984 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5985 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5987 XMLNode &before = the_list->get_state();
5988 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5989 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5991 if (add_p || add_q) {
5992 _editor->session()->add_command (
5993 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5997 /* same thing for the end */
6000 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6004 if (j != _lines.end()) {
6005 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6007 /* j is the line that this audio range starts in; fade out of it;
6008 64 samples length plucked out of thin air.
6011 framepos_t b = i->end - 64;
6016 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6017 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6019 XMLNode &before = the_list->get_state();
6020 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6021 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6023 if (add_p || add_q) {
6024 _editor->session()->add_command (
6025 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6030 _nothing_to_drag = true;
6032 /* Find all the points that should be dragged and put them in the relevant
6033 points lists in the Line structs.
6036 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6038 uint32_t const N = i->line->npoints ();
6039 for (uint32_t j = 0; j < N; ++j) {
6041 /* here's a control point on this line */
6042 ControlPoint* p = i->line->nth (j);
6043 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6045 /* see if it's inside a range */
6046 list<AudioRange>::const_iterator k = _ranges.begin ();
6047 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6051 if (k != _ranges.end()) {
6052 /* dragging this point */
6053 _nothing_to_drag = false;
6054 i->points.push_back (p);
6060 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6061 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6065 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6066 float const f = y_fraction (l->line, current_pointer_y());
6067 /* we are ignoring x position for this drag, so we can just pass in anything */
6068 pair<double, float> result;
6070 result = l->line->drag_motion (0, f, true, false, ignored);
6071 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6076 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6078 if (_nothing_to_drag || !motion_occurred) {
6082 motion (event, false);
6083 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6084 i->line->end_drag (false, 0);
6087 _editor->commit_reversible_command ();
6091 AutomationRangeDrag::aborted (bool)
6093 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6098 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6100 , initial_time_axis_view (itav)
6102 /* note that time_axis_view may be null if the regionview was created
6103 * as part of a copy operation.
6105 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6106 layer = v->region()->layer ();
6107 initial_y = v->get_canvas_group()->position().y;
6108 initial_playlist = v->region()->playlist ();
6109 initial_position = v->region()->position ();
6110 initial_end = v->region()->position () + v->region()->length ();
6113 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6114 : Drag (e, i->canvas_item ())
6117 , _cumulative_dx (0)
6119 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6120 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6125 PatchChangeDrag::motion (GdkEvent* ev, bool)
6127 framepos_t f = adjusted_current_frame (ev);
6128 boost::shared_ptr<Region> r = _region_view->region ();
6129 f = max (f, r->position ());
6130 f = min (f, r->last_frame ());
6132 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6133 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6134 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6135 _cumulative_dx = dxu;
6139 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6141 if (!movement_occurred) {
6142 if (was_double_click()) {
6143 _region_view->edit_patch_change (_patch_change);
6148 boost::shared_ptr<Region> r (_region_view->region ());
6149 framepos_t f = adjusted_current_frame (ev);
6150 f = max (f, r->position ());
6151 f = min (f, r->last_frame ());
6153 _region_view->move_patch_change (
6155 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6160 PatchChangeDrag::aborted (bool)
6162 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6166 PatchChangeDrag::setup_pointer_frame_offset ()
6168 boost::shared_ptr<Region> region = _region_view->region ();
6169 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6172 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6173 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6180 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6182 _region_view->update_drag_selection (
6184 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6188 MidiRubberbandSelectDrag::deselect_things ()
6193 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6194 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6197 _vertical_only = true;
6201 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6203 double const y = _region_view->midi_view()->y_position ();
6205 y1 = max (0.0, y1 - y);
6206 y2 = max (0.0, y2 - y);
6208 _region_view->update_vertical_drag_selection (
6211 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6216 MidiVerticalSelectDrag::deselect_things ()
6221 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6222 : RubberbandSelectDrag (e, i)
6228 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6230 if (drag_in_progress) {
6231 /* We just want to select things at the end of the drag, not during it */
6235 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6237 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6239 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6241 _editor->commit_reversible_selection_op ();
6245 EditorRubberbandSelectDrag::deselect_things ()
6247 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6249 _editor->selection->clear_tracks();
6250 _editor->selection->clear_regions();
6251 _editor->selection->clear_points ();
6252 _editor->selection->clear_lines ();
6253 _editor->selection->clear_midi_notes ();
6255 _editor->commit_reversible_selection_op();
6258 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6263 _note[0] = _note[1] = 0;
6266 NoteCreateDrag::~NoteCreateDrag ()
6272 NoteCreateDrag::grid_frames (framepos_t t) const
6275 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6276 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6278 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6279 - _region_view->region_beats_to_region_frames (t_beats);
6283 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6285 Drag::start_grab (event, cursor);
6287 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6288 TempoMap& map (_editor->session()->tempo_map());
6290 const framepos_t pf = _drags->current_pointer_frame ();
6291 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6293 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6295 double eqaf = map.exact_qn_at_frame (pf, divisions);
6297 if (divisions != 0) {
6299 const double qaf = map.quarter_note_at_frame (pf);
6301 /* Hack so that we always snap to the note that we are over, instead of snapping
6302 to the next one if we're more than halfway through the one we're over.
6305 const double rem = eqaf - qaf;
6307 eqaf -= grid_beats.to_double();
6311 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6312 /* minimum initial length is grid beats */
6313 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6315 double const x0 = _editor->sample_to_pixel (_note[0]);
6316 double const x1 = _editor->sample_to_pixel (_note[1]);
6317 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6319 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6320 _drag_rect->set_outline_all ();
6321 _drag_rect->set_outline_color (0xffffff99);
6322 _drag_rect->set_fill_color (0xffffff66);
6326 NoteCreateDrag::motion (GdkEvent* event, bool)
6328 TempoMap& map (_editor->session()->tempo_map());
6329 const framepos_t pf = _drags->current_pointer_frame ();
6330 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6331 double eqaf = map.exact_qn_at_frame (pf, divisions);
6333 if (divisions != 0) {
6335 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6337 const double qaf = map.quarter_note_at_frame (pf);
6338 /* Hack so that we always snap to the note that we are over, instead of snapping
6339 to the next one if we're more than halfway through the one we're over.
6342 const double rem = eqaf - qaf;
6344 eqaf -= grid_beats.to_double();
6347 eqaf += grid_beats.to_double();
6349 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6351 double const x0 = _editor->sample_to_pixel (_note[0]);
6352 double const x1 = _editor->sample_to_pixel (_note[1]);
6353 _drag_rect->set_x0 (std::min(x0, x1));
6354 _drag_rect->set_x1 (std::max(x0, x1));
6358 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6360 /* we create a note even if there was no movement */
6361 framepos_t const start = min (_note[0], _note[1]);
6362 framepos_t const start_sess_rel = start + _region_view->region()->position();
6363 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6364 framecnt_t const g = grid_frames (start);
6366 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6370 TempoMap& map (_editor->session()->tempo_map());
6371 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6372 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6374 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6378 NoteCreateDrag::y_to_region (double y) const
6381 _region_view->get_canvas_group()->canvas_to_item (x, y);
6386 NoteCreateDrag::aborted (bool)
6391 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6399 HitCreateDrag::~HitCreateDrag ()
6404 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6406 Drag::start_grab (event, cursor);
6408 TempoMap& map (_editor->session()->tempo_map());
6410 const framepos_t pf = _drags->current_pointer_frame ();
6411 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6413 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6415 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6417 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6421 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6422 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6424 Evoral::Beats length = _region_view->get_grid_beats (pf);
6426 _region_view->create_note_at (start, y, length, event->button.state, false);
6433 HitCreateDrag::motion (GdkEvent* event, bool)
6435 TempoMap& map (_editor->session()->tempo_map());
6437 const framepos_t pf = _drags->current_pointer_frame ();
6438 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6440 if (divisions == 0) {
6444 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6445 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6446 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6448 if (_last_pos == start && y == _last_y) {
6452 Evoral::Beats length = _region_view->get_grid_beats (pf);
6454 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6455 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6459 _region_view->create_note_at (start, y, length, event->button.state, false);
6466 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6472 HitCreateDrag::y_to_region (double y) const
6475 _region_view->get_canvas_group()->canvas_to_item (x, y);
6480 HitCreateDrag::aborted (bool)
6485 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6490 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6494 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6496 Drag::start_grab (event, cursor);
6500 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6506 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6509 distance = _drags->current_pointer_x() - grab_x();
6510 len = ar->fade_in()->back()->when;
6512 distance = grab_x() - _drags->current_pointer_x();
6513 len = ar->fade_out()->back()->when;
6516 /* how long should it be ? */
6518 new_length = len + _editor->pixel_to_sample (distance);
6520 /* now check with the region that this is legal */
6522 new_length = ar->verify_xfade_bounds (new_length, start);
6525 arv->reset_fade_in_shape_width (ar, new_length);
6527 arv->reset_fade_out_shape_width (ar, new_length);
6532 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6538 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6541 distance = _drags->current_pointer_x() - grab_x();
6542 len = ar->fade_in()->back()->when;
6544 distance = grab_x() - _drags->current_pointer_x();
6545 len = ar->fade_out()->back()->when;
6548 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6550 _editor->begin_reversible_command ("xfade trim");
6551 ar->playlist()->clear_owned_changes ();
6554 ar->set_fade_in_length (new_length);
6556 ar->set_fade_out_length (new_length);
6559 /* Adjusting the xfade may affect other regions in the playlist, so we need
6560 to get undo Commands from the whole playlist rather than just the
6564 vector<Command*> cmds;
6565 ar->playlist()->rdiff (cmds);
6566 _editor->session()->add_commands (cmds);
6567 _editor->commit_reversible_command ();
6572 CrossfadeEdgeDrag::aborted (bool)
6575 // arv->redraw_start_xfade ();
6577 // arv->redraw_end_xfade ();
6581 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6582 : Drag (e, item, true)
6583 , line (new EditorCursor (*e))
6585 line->set_position (pos);
6587 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6590 RegionCutDrag::~RegionCutDrag ()
6596 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6598 Drag::start_grab (event, c);
6599 motion (event, false);
6603 RegionCutDrag::motion (GdkEvent* event, bool)
6605 MusicFrame pos (_drags->current_pointer_frame(), 0);
6606 _editor->snap_to_with_modifier (pos, event);
6608 line->set_position (pos.frame);
6612 RegionCutDrag::finished (GdkEvent* event, bool)
6614 _editor->get_track_canvas()->canvas()->re_enter();
6617 MusicFrame pos (_drags->current_pointer_frame(), 0);
6618 _editor->snap_to_with_modifier (pos, event);
6621 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos.frame);
6627 _editor->split_regions_at (pos, rs, false);
6631 RegionCutDrag::aborted (bool)
6635 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6636 : Drag (e, item, true)
6641 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6643 Drag::start_grab (event, c);
6645 framepos_t where = _editor->canvas_event_sample(event);
6647 _editor->_dragging_playhead = true;
6649 _editor->playhead_cursor->set_position (where);
6653 RulerZoomDrag::motion (GdkEvent* event, bool)
6655 framepos_t where = _editor->canvas_event_sample(event);
6657 _editor->playhead_cursor->set_position (where);
6659 const double movement_limit = 20.0;
6660 const double scale = 1.08;
6661 const double y_delta = last_pointer_y() - current_pointer_y();
6663 if (y_delta > 0 && y_delta < movement_limit) {
6664 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6665 } else if (y_delta < 0 && y_delta > -movement_limit) {
6666 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6671 RulerZoomDrag::finished (GdkEvent*, bool)
6673 _editor->_dragging_playhead = false;
6675 Session* s = _editor->session ();
6677 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6678 _editor->_pending_locate_request = true;
6684 RulerZoomDrag::aborted (bool)