2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/profile.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/session.h"
44 #include "canvas/canvas.h"
45 #include "canvas/scroll_group.h"
50 #include "audio_region_view.h"
51 #include "automation_region_view.h"
52 #include "midi_region_view.h"
53 #include "ardour_ui.h"
54 #include "gui_thread.h"
55 #include "control_point.h"
56 #include "region_gain_line.h"
57 #include "editor_drag.h"
58 #include "audio_time_axis.h"
59 #include "midi_time_axis.h"
60 #include "selection.h"
61 #include "midi_selection.h"
62 #include "automation_time_axis.h"
64 #include "editor_cursors.h"
65 #include "mouse_cursors.h"
66 #include "note_base.h"
67 #include "patch_change.h"
68 #include "ui_config.h"
69 #include "verbose_cursor.h"
72 using namespace ARDOUR;
75 using namespace Gtkmm2ext;
76 using namespace Editing;
77 using namespace ArdourCanvas;
79 using Gtkmm2ext::Keyboard;
81 double ControlPointDrag::_zero_gain_fraction = -1.0;
83 DragManager::DragManager (Editor* e)
86 , _current_pointer_x (0.0)
87 , _current_pointer_y (0.0)
88 , _current_pointer_frame (0)
89 , _old_follow_playhead (false)
93 DragManager::~DragManager ()
98 /** Call abort for each active drag */
100 DragManager::abort ()
104 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
109 if (!_drags.empty ()) {
110 _editor->set_follow_playhead (_old_follow_playhead, false);
114 _editor->abort_reversible_command();
120 DragManager::add (Drag* d)
122 d->set_manager (this);
123 _drags.push_back (d);
127 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
129 d->set_manager (this);
130 _drags.push_back (d);
135 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
137 /* Prevent follow playhead during the drag to be nice to the user */
138 _old_follow_playhead = _editor->follow_playhead ();
139 _editor->set_follow_playhead (false);
141 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
143 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 (*i)->start_grab (e, c);
148 /** Call end_grab for each active drag.
149 * @return true if any drag reported movement having occurred.
152 DragManager::end_grab (GdkEvent* e)
157 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
158 bool const t = (*i)->end_grab (e);
169 _editor->set_follow_playhead (_old_follow_playhead, false);
175 DragManager::mark_double_click ()
177 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
178 (*i)->set_double_click (true);
183 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
187 /* calling this implies that we expect the event to have canvas
190 * Can we guarantee that this is true?
193 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
195 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
196 bool const t = (*i)->motion_handler (e, from_autoscroll);
197 /* run all handlers; return true if at least one of them
198 returns true (indicating that the event has been handled).
210 DragManager::have_item (ArdourCanvas::Item* i) const
212 list<Drag*>::const_iterator j = _drags.begin ();
213 while (j != _drags.end() && (*j)->item () != i) {
217 return j != _drags.end ();
220 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
224 , _pointer_frame_offset (0)
225 , _x_constrained (false)
226 , _y_constrained (false)
227 , _was_rolling (false)
228 , _trackview_only (trackview_only)
229 , _move_threshold_passed (false)
230 , _starting_point_passed (false)
231 , _initially_vertical (false)
232 , _was_double_click (false)
235 , _last_pointer_x (0.0)
236 , _last_pointer_y (0.0)
237 , _raw_grab_frame (0)
239 , _last_pointer_frame (0)
241 , _snap_delta_music (0.0)
242 , _constraint_pressed (false)
248 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
254 _cursor_ctx = CursorContext::create (*_editor, cursor);
256 _cursor_ctx->change (cursor);
263 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
266 /* we set up x/y dragging constraints on first move */
267 _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
269 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
271 setup_pointer_frame_offset ();
272 _grab_frame = adjusted_frame (_raw_grab_frame, event).frame;
273 _last_pointer_frame = _grab_frame;
274 _last_pointer_x = _grab_x;
276 if (_trackview_only) {
277 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
280 _last_pointer_y = _grab_y;
284 if (!_editor->cursors()->is_invalid (cursor)) {
285 /* CAIROCANVAS need a variant here that passes *cursor */
286 _cursor_ctx = CursorContext::create (*_editor, cursor);
289 if (_editor->session() && _editor->session()->transport_rolling()) {
292 _was_rolling = false;
295 switch (_editor->snap_type()) {
296 case SnapToRegionStart:
297 case SnapToRegionEnd:
298 case SnapToRegionSync:
299 case SnapToRegionBoundary:
300 _editor->build_region_boundary_cache ();
307 /** Call to end a drag `successfully'. Ungrabs item and calls
308 * subclass' finished() method.
310 * @param event GDK event, or 0.
311 * @return true if some movement occurred, otherwise false.
314 Drag::end_grab (GdkEvent* event)
316 _editor->stop_canvas_autoscroll ();
320 finished (event, _move_threshold_passed);
322 _editor->verbose_cursor()->hide ();
325 return _move_threshold_passed;
329 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
331 MusicFrame pos (0, 0);
333 if (f > _pointer_frame_offset) {
334 pos.frame = f - _pointer_frame_offset;
338 _editor->snap_to_with_modifier (pos, event);
345 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
347 return adjusted_frame (_drags->current_pointer_frame (), event, snap).frame;
351 Drag::snap_delta (guint state) const
353 if (ArdourKeyboard::indicates_snap_delta (state)) {
360 Drag::snap_delta_music (guint state) const
362 if (ArdourKeyboard::indicates_snap_delta (state)) {
363 return _snap_delta_music;
370 Drag::current_pointer_x() const
372 return _drags->current_pointer_x ();
376 Drag::current_pointer_y () const
378 if (!_trackview_only) {
379 return _drags->current_pointer_y ();
382 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
386 Drag::setup_snap_delta (MusicFrame pos)
388 TempoMap& map (_editor->session()->tempo_map());
389 MusicFrame snap (pos);
390 _editor->snap_to (snap, ARDOUR::RoundNearest, false, true);
391 _snap_delta = snap.frame - pos.frame;
393 _snap_delta_music = 0.0;
395 if (_snap_delta != 0) {
396 _snap_delta_music = map.exact_qn_at_frame (snap.frame, snap.division) - map.exact_qn_at_frame (pos.frame, pos.division);
401 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
403 /* check to see if we have moved in any way that matters since the last motion event */
404 if (_move_threshold_passed &&
405 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
406 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
410 pair<framecnt_t, int> const threshold = move_threshold ();
412 bool const old_move_threshold_passed = _move_threshold_passed;
414 if (!_move_threshold_passed) {
416 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
417 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
419 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
422 if (active (_editor->mouse_mode) && _move_threshold_passed) {
424 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
426 if (old_move_threshold_passed != _move_threshold_passed) {
430 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
431 _initially_vertical = true;
433 _initially_vertical = false;
435 /** check constraints for this drag.
436 * Note that the current convention is to use "contains" for
437 * key modifiers during motion and "equals" when initiating a drag.
438 * In this case we haven't moved yet, so "equals" applies here.
440 if (Config->get_edit_mode() != Lock) {
441 if (event->motion.state & Gdk::BUTTON2_MASK) {
442 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
443 if (_constraint_pressed) {
444 _x_constrained = false;
445 _y_constrained = true;
447 _x_constrained = true;
448 _y_constrained = false;
450 } else if (_constraint_pressed) {
451 // if dragging normally, the motion is constrained to the first direction of movement.
452 if (_initially_vertical) {
453 _x_constrained = true;
454 _y_constrained = false;
456 _x_constrained = false;
457 _y_constrained = true;
461 if (event->button.state & Gdk::BUTTON2_MASK) {
462 _x_constrained = false;
464 _x_constrained = true;
466 _y_constrained = false;
470 if (!from_autoscroll) {
471 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
474 if (!_editor->autoscroll_active() || from_autoscroll) {
477 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
479 motion (event, first_move && !_starting_point_passed);
481 if (first_move && !_starting_point_passed) {
482 _starting_point_passed = true;
485 _last_pointer_x = _drags->current_pointer_x ();
486 _last_pointer_y = current_pointer_y ();
487 _last_pointer_frame = adjusted_current_frame (event, false);
497 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
505 aborted (_move_threshold_passed);
507 _editor->stop_canvas_autoscroll ();
508 _editor->verbose_cursor()->hide ();
512 Drag::show_verbose_cursor_time (framepos_t frame)
514 _editor->verbose_cursor()->set_time (frame);
515 _editor->verbose_cursor()->show ();
519 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
521 _editor->verbose_cursor()->set_duration (start, end);
522 _editor->verbose_cursor()->show ();
526 Drag::show_verbose_cursor_text (string const & text)
528 _editor->verbose_cursor()->set (text);
529 _editor->verbose_cursor()->show ();
532 boost::shared_ptr<Region>
533 Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
535 if (_editor->session()) {
536 const TempoMap& map (_editor->session()->tempo_map());
537 framecnt_t pos = grab_frame();
538 /* not that the frame rate used here can be affected by pull up/down which
541 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
542 return view->add_region (grab_frame(), len, commit);
545 return boost::shared_ptr<Region>();
548 struct PresentationInfoTimeAxisViewSorter {
549 bool operator() (TimeAxisView* a, TimeAxisView* b) {
550 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
554 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
559 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
561 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
562 as some of the regions we are dragging may be on such tracks.
565 TrackViewList track_views = _editor->track_views;
566 track_views.sort (PresentationInfoTimeAxisViewSorter ());
568 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
569 _time_axis_views.push_back (*i);
571 TimeAxisView::Children children_list = (*i)->get_child_list ();
572 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
573 _time_axis_views.push_back (j->get());
577 /* the list of views can be empty at this point if this is a region list-insert drag
580 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
581 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
584 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
588 RegionDrag::region_going_away (RegionView* v)
590 list<DraggingView>::iterator i = _views.begin ();
591 while (i != _views.end() && i->view != v) {
595 if (i != _views.end()) {
600 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
601 * or -1 if it is not found.
604 RegionDrag::find_time_axis_view (TimeAxisView* t) const
607 int const N = _time_axis_views.size ();
608 while (i < N && _time_axis_views[i] != t) {
612 if (_time_axis_views[i] != t) {
619 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
620 : RegionDrag (e, i, p, v)
622 , _ignore_video_lock (false)
623 , _last_position (0, 0)
625 , _last_pointer_time_axis_view (0)
626 , _last_pointer_layer (0)
631 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
635 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
637 Drag::start_grab (event, cursor);
638 setup_snap_delta (_last_position);
640 show_verbose_cursor_time (_last_position.frame);
642 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
644 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
645 assert(_last_pointer_time_axis_view >= 0);
646 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
649 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
650 _ignore_video_lock = true;
654 /* cross track dragging seems broken here. disabled for now. */
655 _y_constrained = true;
660 RegionMotionDrag::compute_x_delta (GdkEvent const * event, MusicFrame* pending_region_position)
662 /* compute the amount of pointer motion in frames, and where
663 the region would be if we moved it by that much.
665 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
667 framecnt_t sync_offset;
670 sync_offset = _primary->region()->sync_offset (sync_dir);
672 /* we don't handle a sync point that lies before zero.
674 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position->frame >= sync_offset)) {
676 framecnt_t const sd = snap_delta (event->button.state);
677 MusicFrame sync_snap (pending_region_position->frame + (sync_dir * sync_offset) + sd, 0);
678 _editor->snap_to_with_modifier (sync_snap, event);
679 if (sync_offset == 0 && sd == 0) {
680 *pending_region_position = sync_snap;
682 pending_region_position->set (_primary->region()->adjust_to_sync (sync_snap.frame) - sd, 0);
685 *pending_region_position = _last_position;
688 if (pending_region_position->frame > max_framepos - _primary->region()->length()) {
689 *pending_region_position = _last_position;
694 bool const x_move_allowed = !_x_constrained;
696 if ((pending_region_position->frame != _last_position.frame) && x_move_allowed) {
698 /* x movement since last time (in pixels) */
699 dx = _editor->sample_to_pixel_unrounded (pending_region_position->frame - _last_position.frame);
701 /* total x movement */
702 framecnt_t total_dx = _editor->pixel_to_sample (_total_x_delta + dx);
704 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
705 frameoffset_t const off = i->view->region()->position() + total_dx;
707 dx = dx - _editor->sample_to_pixel_unrounded (off);
708 *pending_region_position = MusicFrame (pending_region_position->frame - off, 0);
718 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
724 const int tavsize = _time_axis_views.size();
725 const int dt = delta > 0 ? +1 : -1;
727 int target = start + delta - skip;
729 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
730 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
732 while (current >= 0 && current != target) {
734 if (current < 0 && dt < 0) {
737 if (current >= tavsize && dt > 0) {
740 if (current < 0 || current >= tavsize) {
744 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
745 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
749 if (distance_only && current == start + delta) {
757 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
759 if (_y_constrained) {
763 const int tavsize = _time_axis_views.size();
764 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
765 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
766 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
768 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
769 /* already in the drop zone */
770 if (delta_track >= 0) {
771 /* downward motion - OK if others are still not in the dropzone */
780 } else if (n >= tavsize) {
781 /* downward motion into drop zone. That's fine. */
785 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
786 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
787 /* not a track, or the wrong type */
791 double const l = i->layer + delta_layer;
793 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
794 mode to allow the user to place a region below another on layer 0.
796 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
797 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
798 If it has, the layers will be munged later anyway, so it's ok.
804 /* all regions being dragged are ok with this change */
808 struct DraggingViewSorter {
809 bool operator() (const DraggingView& a, const DraggingView& b) {
810 return a.time_axis_view < b.time_axis_view;
815 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
817 double delta_layer = 0;
818 int delta_time_axis_view = 0;
819 int current_pointer_time_axis_view = -1;
821 assert (!_views.empty ());
823 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
825 /* Find the TimeAxisView that the pointer is now over */
826 const double cur_y = current_pointer_y ();
827 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
828 TimeAxisView* tv = r.first;
830 if (!tv && cur_y < 0) {
831 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
835 /* find drop-zone y-position */
836 Coord last_track_bottom_edge;
837 last_track_bottom_edge = 0;
838 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
839 if (!(*t)->hidden()) {
840 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
845 if (tv && tv->view()) {
846 /* the mouse is over a track */
847 double layer = r.second;
849 if (first_move && tv->view()->layer_display() == Stacked) {
850 tv->view()->set_layer_display (Expanded);
853 /* Here's the current pointer position in terms of time axis view and layer */
854 current_pointer_time_axis_view = find_time_axis_view (tv);
855 assert(current_pointer_time_axis_view >= 0);
857 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
859 /* Work out the change in y */
861 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
862 if (!rtv || !rtv->is_track()) {
863 /* ignore non-tracks early on. we can't move any regions on them */
864 } else if (_last_pointer_time_axis_view < 0) {
865 /* Was in the drop-zone, now over a track.
866 * Hence it must be an upward move (from the bottom)
868 * track_index is still -1, so delta must be set to
869 * move up the correct number of tracks from the bottom.
871 * This is necessary because steps may be skipped if
872 * the bottom-most track is not a valid target and/or
873 * if there are hidden tracks at the bottom.
874 * Hence the initial offset (_ddropzone) as well as the
875 * last valid pointer position (_pdropzone) need to be
876 * taken into account.
878 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
880 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
883 /* TODO needs adjustment per DraggingView,
885 * e.g. select one region on the top-layer of a track
886 * and one region which is at the bottom-layer of another track
889 * Indicated drop-zones and layering is wrong.
890 * and may infer additional layers on the target-track
891 * (depending how many layers the original track had).
893 * Or select two regions (different layers) on a same track,
894 * move across a non-layer track.. -> layering info is lost.
895 * on drop either of the regions may be on top.
897 * Proposed solution: screw it :) well,
898 * don't use delta_layer, use an absolute value
899 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
900 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
901 * 3) iterate over all DraggingView, find the one that is over the track with most layers
902 * 4) proportionally scale layer to layers available on target
904 delta_layer = current_pointer_layer - _last_pointer_layer;
907 /* for automation lanes, there is a TimeAxisView but no ->view()
908 * if (!tv) -> dropzone
910 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
911 /* Moving into the drop-zone.. */
912 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
913 /* delta_time_axis_view may not be sufficient to move into the DZ
914 * the mouse may enter it, but it may not be a valid move due to
917 * -> remember the delta needed to move into the dropzone
919 _ddropzone = delta_time_axis_view;
920 /* ..but subtract hidden tracks (or routes) at the bottom.
921 * we silently move mover them
923 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
924 - _time_axis_views.size();
926 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
927 /* move around inside the zone.
928 * This allows to move further down until all regions are in the zone.
930 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
931 assert(ptr_y >= last_track_bottom_edge);
932 assert(_ddropzone > 0);
934 /* calculate mouse position in 'tracks' below last track. */
935 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
936 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
938 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
940 delta_time_axis_view = dzpos - _pdropzone;
941 } else if (dzpos < _pdropzone && _ndropzone > 0) {
942 // move up inside the DZ
943 delta_time_axis_view = dzpos - _pdropzone;
947 /* Work out the change in x */
948 TempoMap& tmap = _editor->session()->tempo_map();
949 MusicFrame pending_region_position (0, 0);
950 double const x_delta = compute_x_delta (event, &pending_region_position);
952 double const last_pos_qn = tmap.exact_qn_at_frame (_last_position.frame, _last_position.division);
953 double const qn_delta = tmap.exact_qn_at_frame (pending_region_position.frame, pending_region_position.division) - last_pos_qn;
955 _last_position = pending_region_position;
957 /* calculate hidden tracks in current y-axis delta */
959 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
960 /* The mouse is more than one track below the dropzone.
961 * distance calculation is not needed (and would not work, either
962 * because the dropzone is "packed").
964 * Except when [partially] moving regions out of dropzone in a large step.
965 * (the mouse may or may not remain in the DZ)
966 * Hidden tracks at the bottom of the TAV need to be skipped.
968 * This also handles the case if the mouse entered the DZ
969 * in a large step (exessive delta), either due to fast-movement,
970 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
972 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
973 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
975 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
976 -_time_axis_views.size() - dt;
979 else if (_last_pointer_time_axis_view < 0) {
980 /* Moving out of the zone. Check for hidden tracks at the bottom. */
981 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
982 -_time_axis_views.size() - delta_time_axis_view;
984 /* calculate hidden tracks that are skipped by the pointer movement */
985 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
986 - _last_pointer_time_axis_view
987 - delta_time_axis_view;
990 /* Verify change in y */
991 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
992 /* this y movement is not allowed, so do no y movement this time */
993 delta_time_axis_view = 0;
998 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
999 /* haven't reached next snap point, and we're not switching
1000 trackviews nor layers. nothing to do.
1005 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
1006 PlaylistDropzoneMap playlist_dropzone_map;
1007 _ndropzone = 0; // number of elements currently in the dropzone
1010 /* sort views by time_axis.
1011 * This retains track order in the dropzone, regardless
1012 * of actual selection order
1014 _views.sort (DraggingViewSorter());
1016 /* count number of distinct tracks of all regions
1017 * being dragged, used for dropzone.
1019 int prev_track = -1;
1020 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1021 if (i->time_axis_view != prev_track) {
1022 prev_track = i->time_axis_view;
1028 _views.back().time_axis_view -
1029 _views.front().time_axis_view;
1031 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1032 - _views.back().time_axis_view;
1034 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1038 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1040 RegionView* rv = i->view;
1045 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1052 /* reparent the regionview into a group above all
1056 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1057 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1058 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1059 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1060 /* move the item so that it continues to appear at the
1061 same location now that its parent has changed.
1063 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1066 /* If we have moved tracks, we'll fudge the layer delta so that the
1067 region gets moved back onto layer 0 on its new track; this avoids
1068 confusion when dragging regions from non-zero layers onto different
1071 double this_delta_layer = delta_layer;
1072 if (delta_time_axis_view != 0) {
1073 this_delta_layer = - i->layer;
1076 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1078 int track_index = i->time_axis_view + this_delta_time_axis_view;
1079 assert(track_index >= 0);
1081 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1082 /* Track is in the Dropzone */
1084 i->time_axis_view = track_index;
1085 assert(i->time_axis_view >= (int) _time_axis_views.size());
1088 double yposition = 0;
1089 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1090 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1093 /* store index of each new playlist as a negative count, starting at -1 */
1095 if (pdz == playlist_dropzone_map.end()) {
1096 /* compute where this new track (which doesn't exist yet) will live
1099 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1101 /* How high is this region view ? */
1103 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1104 ArdourCanvas::Rect bbox;
1107 bbox = obbox.get ();
1110 last_track_bottom_edge += bbox.height();
1112 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1115 yposition = pdz->second;
1118 /* values are zero or negative, hence the use of min() */
1119 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1122 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1124 mrv->apply_note_range (60, 71, true);
1128 /* The TimeAxisView that this region is now over */
1129 TimeAxisView* current_tv = _time_axis_views[track_index];
1131 /* Ensure it is moved from stacked -> expanded if appropriate */
1132 if (current_tv->view()->layer_display() == Stacked) {
1133 current_tv->view()->set_layer_display (Expanded);
1136 /* We're only allowed to go -ve in layer on Expanded views */
1137 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1138 this_delta_layer = - i->layer;
1142 rv->set_height (current_tv->view()->child_height ());
1144 /* Update show/hidden status as the region view may have come from a hidden track,
1145 or have moved to one.
1147 if (current_tv->hidden ()) {
1148 rv->get_canvas_group()->hide ();
1150 rv->get_canvas_group()->show ();
1153 /* Update the DraggingView */
1154 i->time_axis_view = track_index;
1155 i->layer += this_delta_layer;
1158 _editor->mouse_brush_insert_region (rv, pending_region_position.frame);
1162 /* Get the y coordinate of the top of the track that this region is now over */
1163 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1165 /* And adjust for the layer that it should be on */
1166 StreamView* cv = current_tv->view ();
1167 switch (cv->layer_display ()) {
1171 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1174 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1178 /* need to get the parent of the regionview
1179 * canvas group and get its position in
1180 * equivalent coordinate space as the trackview
1181 * we are now dragging over.
1184 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1188 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1190 MidiStreamView* msv;
1191 if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
1192 mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
1197 /* Now move the region view */
1198 if (rv->region()->position_lock_style() == MusicTime) {
1199 double const last_qn = tmap.quarter_note_at_frame (rv->get_position());
1200 framepos_t const x_pos_music = tmap.frame_at_quarter_note (last_qn + qn_delta);
1202 rv->set_position (x_pos_music, 0);
1204 rv->move (x_delta, y_delta);
1207 } /* foreach region */
1209 _total_x_delta += x_delta;
1211 if (x_delta != 0 && !_brushing) {
1212 show_verbose_cursor_time (_last_position.frame);
1215 /* keep track of pointer movement */
1217 /* the pointer is currently over a time axis view */
1219 if (_last_pointer_time_axis_view < 0) {
1220 /* last motion event was not over a time axis view
1221 * or last y-movement out of the dropzone was not valid
1224 if (delta_time_axis_view < 0) {
1225 /* in the drop zone, moving up */
1227 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1228 * We do not use negative _last_pointer_time_axis_view because
1229 * the dropzone is "packed" (the actual track offset is ignored)
1231 * As opposed to the actual number
1232 * of elements in the dropzone (_ndropzone)
1233 * _pdropzone is not constrained. This is necessary
1234 * to allow moving multiple regions with y-distance
1237 * There can be 0 elements in the dropzone,
1238 * even though the drag-pointer is inside the DZ.
1241 * [ Audio-track, Midi-track, Audio-track, DZ ]
1242 * move regions from both audio tracks at the same time into the
1243 * DZ by grabbing the region in the bottom track.
1245 assert(current_pointer_time_axis_view >= 0);
1246 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1250 /* only move out of the zone if the movement is OK */
1251 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1252 assert(delta_time_axis_view < 0);
1253 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1254 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1255 * the current position can be calculated as follows:
1257 // a well placed oofus attack can still throw this off.
1258 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1259 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1262 /* last motion event was also over a time axis view */
1263 _last_pointer_time_axis_view += delta_time_axis_view;
1264 assert(_last_pointer_time_axis_view >= 0);
1269 /* the pointer is not over a time axis view */
1270 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1271 _pdropzone += delta_time_axis_view - delta_skip;
1272 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1275 _last_pointer_layer += delta_layer;
1279 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1281 if (_copy && first_move) {
1282 if (_x_constrained && !_brushing) {
1283 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1284 } else if (!_brushing) {
1285 _editor->begin_reversible_command (Operations::region_copy);
1286 } else if (_brushing) {
1287 _editor->begin_reversible_command (Operations::drag_region_brush);
1289 /* duplicate the regionview(s) and region(s) */
1291 list<DraggingView> new_regionviews;
1293 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1295 RegionView* rv = i->view;
1296 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1297 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1299 const boost::shared_ptr<const Region> original = rv->region();
1300 boost::shared_ptr<Region> region_copy;
1302 region_copy = RegionFactory::create (original, true);
1304 /* need to set this so that the drop zone code can work. This doesn't
1305 actually put the region into the playlist, but just sets a weak pointer
1308 region_copy->set_playlist (original->playlist());
1312 boost::shared_ptr<AudioRegion> audioregion_copy
1313 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1315 nrv = new AudioRegionView (*arv, audioregion_copy);
1317 boost::shared_ptr<MidiRegion> midiregion_copy
1318 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1319 nrv = new MidiRegionView (*mrv, midiregion_copy);
1324 nrv->get_canvas_group()->show ();
1325 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1327 /* swap _primary to the copy */
1329 if (rv == _primary) {
1333 /* ..and deselect the one we copied */
1335 rv->set_selected (false);
1338 if (!new_regionviews.empty()) {
1340 /* reflect the fact that we are dragging the copies */
1342 _views = new_regionviews;
1344 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1347 } else if (!_copy && first_move) {
1348 if (_x_constrained && !_brushing) {
1349 _editor->begin_reversible_command (_("fixed time region drag"));
1350 } else if (!_brushing) {
1351 _editor->begin_reversible_command (Operations::region_drag);
1352 } else if (_brushing) {
1353 _editor->begin_reversible_command (Operations::drag_region_brush);
1356 RegionMotionDrag::motion (event, first_move);
1360 RegionMotionDrag::finished (GdkEvent *, bool)
1362 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1363 if (!(*i)->view()) {
1367 if ((*i)->view()->layer_display() == Expanded) {
1368 (*i)->view()->set_layer_display (Stacked);
1374 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1376 RegionMotionDrag::finished (ev, movement_occurred);
1378 if (!movement_occurred) {
1382 if (was_double_click() && !_views.empty()) {
1383 DraggingView dv = _views.front();
1384 _editor->edit_region (dv.view);
1390 assert (!_views.empty ());
1392 /* We might have hidden region views so that they weren't visible during the drag
1393 (when they have been reparented). Now everything can be shown again, as region
1394 views are back in their track parent groups.
1396 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1397 i->view->get_canvas_group()->show ();
1400 bool const changed_position = (_last_position.frame != _primary->region()->position());
1401 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1425 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1427 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1430 TimeAxisView* tav = 0;
1432 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1433 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1434 uint32_t output_chan = region->n_channels();
1435 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1436 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1438 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1439 tav =_editor->axis_view_from_stripable (audio_tracks.front());
1441 ChanCount one_midi_port (DataType::MIDI, 1);
1442 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1443 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port,
1444 Config->get_strict_io () || Profile->get_mixbus (),
1445 boost::shared_ptr<ARDOUR::PluginInfo>(),
1446 (ARDOUR::Plugin::PresetRecord*) 0,
1447 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1448 tav = _editor->axis_view_from_stripable (midi_tracks.front());
1452 tav->set_height (original->current_height());
1455 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1458 return dynamic_cast<RouteTimeAxisView*> (tav);
1462 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, MusicFrame last_position, int32_t const ev_state)
1464 RegionSelection new_views;
1465 PlaylistSet modified_playlists;
1466 RouteTimeAxisView* new_time_axis_view = 0;
1467 framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1469 TempoMap& tmap (_editor->session()->tempo_map());
1470 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1471 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1474 /* all changes were made during motion event handlers */
1476 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1480 _editor->commit_reversible_command ();
1484 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1485 PlaylistMapping playlist_mapping;
1487 /* insert the regions into their new playlists */
1488 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1490 RouteTimeAxisView* dest_rtv = 0;
1492 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1496 MusicFrame where (0, 0);
1497 double quarter_note;
1499 if (changed_position && !_x_constrained) {
1500 where.set (i->view->region()->position() - drag_delta, 0);
1501 quarter_note = i->view->region()->quarter_note() - qn_delta;
1503 /* region has not moved - divisor will not affect musical pos */
1504 where.set (i->view->region()->position(), 0);
1505 quarter_note = i->view->region()->quarter_note();
1508 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1509 /* dragged to drop zone */
1511 PlaylistMapping::iterator pm;
1513 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1514 /* first region from this original playlist: create a new track */
1515 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1516 if(!new_time_axis_view) {
1520 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1521 dest_rtv = new_time_axis_view;
1523 /* we already created a new track for regions from this playlist, use it */
1524 dest_rtv = pm->second;
1527 /* destination time axis view is the one we dragged to */
1528 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1531 if (dest_rtv != 0) {
1532 RegionView* new_view;
1533 if (i->view == _primary && !_x_constrained) {
1534 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, last_position, last_pos_qn,
1535 modified_playlists, true);
1537 if (i->view->region()->position_lock_style() == AudioTime) {
1538 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1539 modified_playlists);
1541 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1542 modified_playlists, true);
1546 if (new_view != 0) {
1547 new_views.push_back (new_view);
1551 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1552 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1555 list<DraggingView>::const_iterator next = i;
1561 /* If we've created new regions either by copying or moving
1562 to a new track, we want to replace the old selection with the new ones
1565 if (new_views.size() > 0) {
1566 _editor->selection->set (new_views);
1569 /* write commands for the accumulated diffs for all our modified playlists */
1570 add_stateful_diff_commands_for_playlists (modified_playlists);
1572 _editor->commit_reversible_command ();
1576 RegionMoveDrag::finished_no_copy (
1577 bool const changed_position,
1578 bool const changed_tracks,
1579 MusicFrame last_position,
1580 int32_t const ev_state
1583 RegionSelection new_views;
1584 PlaylistSet modified_playlists;
1585 PlaylistSet frozen_playlists;
1586 set<RouteTimeAxisView*> views_to_update;
1587 RouteTimeAxisView* new_time_axis_view = 0;
1588 framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1590 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1591 PlaylistMapping playlist_mapping;
1593 TempoMap& tmap (_editor->session()->tempo_map());
1594 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1595 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1597 std::set<boost::shared_ptr<const Region> > uniq;
1598 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1600 RegionView* rv = i->view;
1601 RouteTimeAxisView* dest_rtv = 0;
1603 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1608 if (uniq.find (rv->region()) != uniq.end()) {
1609 /* prevent duplicate moves when selecting regions from shared playlists */
1613 uniq.insert(rv->region());
1615 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1616 /* dragged to drop zone */
1618 PlaylistMapping::iterator pm;
1620 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1621 /* first region from this original playlist: create a new track */
1622 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1623 if(!new_time_axis_view) { // New track creation failed
1627 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1628 dest_rtv = new_time_axis_view;
1630 /* we already created a new track for regions from this playlist, use it */
1631 dest_rtv = pm->second;
1635 /* destination time axis view is the one we dragged to */
1636 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1641 double const dest_layer = i->layer;
1643 views_to_update.insert (dest_rtv);
1645 MusicFrame where (0, 0);
1646 double quarter_note;
1648 if (changed_position && !_x_constrained) {
1649 where.set (rv->region()->position() - drag_delta, 0);
1650 quarter_note = i->view->region()->quarter_note() - qn_delta;
1652 where.set (rv->region()->position(), 0);
1653 quarter_note = i->view->region()->quarter_note();
1656 if (changed_tracks) {
1658 /* insert into new playlist */
1659 RegionView* new_view;
1660 if (rv == _primary) {
1661 new_view = insert_region_into_playlist (
1662 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, last_position, last_pos_qn,
1663 modified_playlists, true
1666 if (rv->region()->position_lock_style() == AudioTime) {
1668 new_view = insert_region_into_playlist (
1669 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1673 new_view = insert_region_into_playlist (
1674 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1675 modified_playlists, true
1680 if (new_view == 0) {
1685 new_views.push_back (new_view);
1687 /* remove from old playlist */
1689 /* the region that used to be in the old playlist is not
1690 moved to the new one - we use a copy of it. as a result,
1691 any existing editor for the region should no longer be
1694 rv->hide_region_editor();
1697 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1701 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1703 /* this movement may result in a crossfade being modified, or a layering change,
1704 so we need to get undo data from the playlist as well as the region.
1707 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1709 playlist->clear_changes ();
1712 rv->region()->clear_changes ();
1715 motion on the same track. plonk the previously reparented region
1716 back to its original canvas group (its streamview).
1717 No need to do anything for copies as they are fake regions which will be deleted.
1720 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1721 rv->get_canvas_group()->set_y_position (i->initial_y);
1724 /* just change the model */
1725 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1726 playlist->set_layer (rv->region(), dest_layer);
1729 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1731 r = frozen_playlists.insert (playlist);
1734 playlist->freeze ();
1736 if (rv == _primary) {
1737 rv->region()->set_position (where.frame, last_position.division);
1739 if (rv->region()->position_lock_style() == AudioTime) {
1740 /* move by frame offset */
1741 rv->region()->set_position (where.frame, 0);
1743 /* move by music offset */
1744 rv->region()->set_position_music (rv->region()->quarter_note() - qn_delta);
1747 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1750 if (changed_tracks) {
1752 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1753 was selected in all of them, then removing it from a playlist will have removed all
1754 trace of it from _views (i.e. there were N regions selected, we removed 1,
1755 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1756 corresponding regionview, and _views is now empty).
1758 This could have invalidated any and all iterators into _views.
1760 The heuristic we use here is: if the region selection is empty, break out of the loop
1761 here. if the region selection is not empty, then restart the loop because we know that
1762 we must have removed at least the region(view) we've just been working on as well as any
1763 that we processed on previous iterations.
1765 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1766 we can just iterate.
1770 if (_views.empty()) {
1781 /* If we've created new regions either by copying or moving
1782 to a new track, we want to replace the old selection with the new ones
1785 if (new_views.size() > 0) {
1786 _editor->selection->set (new_views);
1789 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1793 /* write commands for the accumulated diffs for all our modified playlists */
1794 add_stateful_diff_commands_for_playlists (modified_playlists);
1795 /* applies to _brushing */
1796 _editor->commit_reversible_command ();
1798 /* We have futzed with the layering of canvas items on our streamviews.
1799 If any region changed layer, this will have resulted in the stream
1800 views being asked to set up their region views, and all will be well.
1801 If not, we might now have badly-ordered region views. Ask the StreamViews
1802 involved to sort themselves out, just in case.
1805 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1806 (*i)->view()->playlist_layered ((*i)->track ());
1810 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1811 * @param region Region to remove.
1812 * @param playlist playlist To remove from.
1813 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1814 * that clear_changes () is only called once per playlist.
1817 RegionMoveDrag::remove_region_from_playlist (
1818 boost::shared_ptr<Region> region,
1819 boost::shared_ptr<Playlist> playlist,
1820 PlaylistSet& modified_playlists
1823 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1826 playlist->clear_changes ();
1829 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1833 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1834 * clearing the playlist's diff history first if necessary.
1835 * @param region Region to insert.
1836 * @param dest_rtv Destination RouteTimeAxisView.
1837 * @param dest_layer Destination layer.
1838 * @param where Destination position.
1839 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1840 * that clear_changes () is only called once per playlist.
1841 * @return New RegionView, or 0 if no insert was performed.
1844 RegionMoveDrag::insert_region_into_playlist (
1845 boost::shared_ptr<Region> region,
1846 RouteTimeAxisView* dest_rtv,
1849 double quarter_note,
1850 PlaylistSet& modified_playlists,
1854 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1855 if (!dest_playlist) {
1859 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1860 _new_region_view = 0;
1861 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1863 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1864 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1866 dest_playlist->clear_changes ();
1869 dest_playlist->add_region (region, where.frame, 1.0, false, where.division, quarter_note, true);
1871 dest_playlist->add_region (region, where.frame, 1.0, false, where.division);
1874 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1875 dest_playlist->set_layer (region, dest_layer);
1880 assert (_new_region_view);
1882 return _new_region_view;
1886 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1888 _new_region_view = rv;
1892 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1894 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1895 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1897 _editor->session()->add_command (c);
1906 RegionMoveDrag::aborted (bool movement_occurred)
1910 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1911 list<DraggingView>::const_iterator next = i;
1920 RegionMotionDrag::aborted (movement_occurred);
1925 RegionMotionDrag::aborted (bool)
1927 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1929 StreamView* sview = (*i)->view();
1932 if (sview->layer_display() == Expanded) {
1933 sview->set_layer_display (Stacked);
1938 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1939 RegionView* rv = i->view;
1940 TimeAxisView* tv = &(rv->get_time_axis_view ());
1941 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1943 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1944 rv->get_canvas_group()->set_y_position (0);
1946 rv->move (-_total_x_delta, 0);
1947 rv->set_height (rtv->view()->child_height ());
1951 /** @param b true to brush, otherwise false.
1952 * @param c true to make copies of the regions being moved, otherwise false.
1954 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1955 : RegionMotionDrag (e, i, p, v, b)
1957 , _new_region_view (0)
1959 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1962 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1963 if (rtv && rtv->is_track()) {
1964 speed = rtv->track()->speed ();
1967 _last_position = MusicFrame (static_cast<framepos_t> (_primary->region()->position() / speed), 0);
1971 RegionMoveDrag::setup_pointer_frame_offset ()
1973 _pointer_frame_offset = raw_grab_frame() - _last_position.frame;
1976 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1977 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1979 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1981 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1982 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1984 _primary = v->view()->create_region_view (r, false, false);
1986 _primary->get_canvas_group()->show ();
1987 _primary->set_position (pos, 0);
1988 _views.push_back (DraggingView (_primary, this, v));
1990 _last_position = MusicFrame (pos, 0);
1992 _item = _primary->get_canvas_group ();
1996 RegionInsertDrag::finished (GdkEvent * event, bool)
1998 int pos = _views.front().time_axis_view;
1999 assert(pos >= 0 && pos < (int)_time_axis_views.size());
2001 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
2003 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
2004 _primary->get_canvas_group()->set_y_position (0);
2006 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
2008 _editor->begin_reversible_command (Operations::insert_region);
2009 playlist->clear_changes ();
2010 _editor->snap_to_with_modifier (_last_position, event);
2012 playlist->add_region (_primary->region (), _last_position.frame, 1.0, false, _last_position.division);
2014 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
2015 if (Config->get_edit_mode() == Ripple) {
2016 playlist->ripple (_last_position.frame, _primary->region()->length(), _primary->region());
2019 _editor->session()->add_command (new StatefulDiffCommand (playlist));
2020 _editor->commit_reversible_command ();
2028 RegionInsertDrag::aborted (bool)
2035 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2036 : RegionMoveDrag (e, i, p, v, false, false)
2038 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
2041 struct RegionSelectionByPosition {
2042 bool operator() (RegionView*a, RegionView* b) {
2043 return a->region()->position () < b->region()->position();
2048 RegionSpliceDrag::motion (GdkEvent* event, bool)
2050 /* Which trackview is this ? */
2052 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2053 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2055 /* The region motion is only processed if the pointer is over
2059 if (!tv || !tv->is_track()) {
2060 /* To make sure we hide the verbose canvas cursor when the mouse is
2061 not held over an audio track.
2063 _editor->verbose_cursor()->hide ();
2066 _editor->verbose_cursor()->show ();
2071 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2077 RegionSelection copy;
2078 _editor->selection->regions.by_position(copy);
2080 framepos_t const pf = adjusted_current_frame (event);
2082 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2084 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2090 boost::shared_ptr<Playlist> playlist;
2092 if ((playlist = atv->playlist()) == 0) {
2096 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2101 if (pf < (*i)->region()->last_frame() + 1) {
2105 if (pf > (*i)->region()->first_frame()) {
2111 playlist->shuffle ((*i)->region(), dir);
2116 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2118 RegionMoveDrag::finished (event, movement_occurred);
2122 RegionSpliceDrag::aborted (bool)
2132 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2135 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2137 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2138 RegionSelection to_ripple;
2139 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2140 if ((*i)->position() >= where) {
2141 to_ripple.push_back (rtv->view()->find_view(*i));
2145 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2146 if (!exclude.contains (*i)) {
2147 // the selection has already been added to _views
2149 if (drag_in_progress) {
2150 // do the same things that RegionMotionDrag::motion does when
2151 // first_move is true, for the region views that we're adding
2152 // to _views this time
2155 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2156 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2157 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2158 rvg->reparent (_editor->_drag_motion_group);
2160 // we only need to move in the y direction
2161 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2166 _views.push_back (DraggingView (*i, this, tav));
2172 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2175 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2176 // we added all the regions after the selection
2178 std::list<DraggingView>::iterator to_erase = i++;
2179 if (!_editor->selection->regions.contains (to_erase->view)) {
2180 // restore the non-selected regions to their original playlist & positions,
2181 // and then ripple them back by the length of the regions that were dragged away
2182 // do the same things as RegionMotionDrag::aborted
2184 RegionView *rv = to_erase->view;
2185 TimeAxisView* tv = &(rv->get_time_axis_view ());
2186 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2189 // plonk them back onto their own track
2190 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2191 rv->get_canvas_group()->set_y_position (0);
2195 // move the underlying region to match the view
2196 rv->region()->set_position (rv->region()->position() + amount);
2198 // restore the view to match the underlying region's original position
2199 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2202 rv->set_height (rtv->view()->child_height ());
2203 _views.erase (to_erase);
2209 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2211 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2213 return allow_moves_across_tracks;
2221 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2222 : RegionMoveDrag (e, i, p, v, false, false)
2224 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2225 // compute length of selection
2226 RegionSelection selected_regions = _editor->selection->regions;
2227 selection_length = selected_regions.end_frame() - selected_regions.start();
2229 // we'll only allow dragging to another track in ripple mode if all the regions
2230 // being dragged start off on the same track
2231 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2234 exclude = new RegionList;
2235 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2236 exclude->push_back((*i)->region());
2239 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2240 RegionSelection copy;
2241 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2243 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2244 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2246 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2247 // find ripple start point on each applicable playlist
2248 RegionView *first_selected_on_this_track = NULL;
2249 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2250 if ((*i)->region()->playlist() == (*pi)) {
2251 // region is on this playlist - it's the first, because they're sorted
2252 first_selected_on_this_track = *i;
2256 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2257 add_all_after_to_views (
2258 &first_selected_on_this_track->get_time_axis_view(),
2259 first_selected_on_this_track->region()->position(),
2260 selected_regions, false);
2263 if (allow_moves_across_tracks) {
2264 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2272 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2274 /* Which trackview is this ? */
2276 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2277 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2279 /* The region motion is only processed if the pointer is over
2283 if (!tv || !tv->is_track()) {
2284 /* To make sure we hide the verbose canvas cursor when the mouse is
2285 not held over an audiotrack.
2287 _editor->verbose_cursor()->hide ();
2291 framepos_t where = adjusted_current_frame (event);
2292 assert (where >= 0);
2293 MusicFrame after (0, 0);
2294 double delta = compute_x_delta (event, &after);
2296 framecnt_t amount = _editor->pixel_to_sample (delta);
2298 if (allow_moves_across_tracks) {
2299 // all the originally selected regions were on the same track
2301 framecnt_t adjust = 0;
2302 if (prev_tav && tv != prev_tav) {
2303 // dragged onto a different track
2304 // remove the unselected regions from _views, restore them to their original positions
2305 // and add the regions after the drop point on the new playlist to _views instead.
2306 // undo the effect of rippling the previous playlist, and include the effect of removing
2307 // the dragged region(s) from this track
2309 remove_unselected_from_views (prev_amount, false);
2310 // ripple previous playlist according to the regions that have been removed onto the new playlist
2311 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2314 // move just the selected regions
2315 RegionMoveDrag::motion(event, first_move);
2317 // ensure that the ripple operation on the new playlist inserts selection_length time
2318 adjust = selection_length;
2319 // ripple the new current playlist
2320 tv->playlist()->ripple (where, amount+adjust, exclude);
2322 // add regions after point where drag entered this track to subsequent ripples
2323 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2326 // motion on same track
2327 RegionMoveDrag::motion(event, first_move);
2331 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2332 prev_position = where;
2334 // selection encompasses multiple tracks - just drag
2335 // cross-track drags are forbidden
2336 RegionMoveDrag::motion(event, first_move);
2339 if (!_x_constrained) {
2340 prev_amount += amount;
2343 _last_position = after;
2347 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2349 if (!movement_occurred) {
2353 if (was_double_click() && !_views.empty()) {
2354 DraggingView dv = _views.front();
2355 _editor->edit_region (dv.view);
2361 _editor->begin_reversible_command(_("Ripple drag"));
2363 // remove the regions being rippled from the dragging view, updating them to
2364 // their new positions
2365 remove_unselected_from_views (prev_amount, true);
2367 if (allow_moves_across_tracks) {
2369 // if regions were dragged across tracks, we've rippled any later
2370 // regions on the track the regions were dragged off, so we need
2371 // to add the original track to the undo record
2372 orig_tav->playlist()->clear_changes();
2373 vector<Command*> cmds;
2374 orig_tav->playlist()->rdiff (cmds);
2375 _editor->session()->add_commands (cmds);
2377 if (prev_tav && prev_tav != orig_tav) {
2378 prev_tav->playlist()->clear_changes();
2379 vector<Command*> cmds;
2380 prev_tav->playlist()->rdiff (cmds);
2381 _editor->session()->add_commands (cmds);
2384 // selection spanned multiple tracks - all will need adding to undo record
2386 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2387 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2389 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2390 (*pi)->clear_changes();
2391 vector<Command*> cmds;
2392 (*pi)->rdiff (cmds);
2393 _editor->session()->add_commands (cmds);
2397 // other modified playlists are added to undo by RegionMoveDrag::finished()
2398 RegionMoveDrag::finished (event, movement_occurred);
2399 _editor->commit_reversible_command();
2403 RegionRippleDrag::aborted (bool movement_occurred)
2405 RegionMoveDrag::aborted (movement_occurred);
2410 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2412 _view (dynamic_cast<MidiTimeAxisView*> (v))
2414 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2420 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2423 _editor->begin_reversible_command (_("create region"));
2424 _region = add_midi_region (_view, false);
2425 _view->playlist()->freeze ();
2428 framepos_t const f = adjusted_current_frame (event);
2429 if (f < grab_frame()) {
2430 _region->set_initial_position (f);
2433 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2434 so that if this region is duplicated, its duplicate starts on
2435 a snap point rather than 1 frame after a snap point. Otherwise things get
2436 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2437 place snapped notes at the start of the region.
2440 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2441 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2447 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2449 if (!movement_occurred) {
2450 add_midi_region (_view, true);
2452 _view->playlist()->thaw ();
2453 _editor->commit_reversible_command();
2458 RegionCreateDrag::aborted (bool)
2461 _view->playlist()->thaw ();
2467 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2472 , _was_selected (false)
2475 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2479 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2481 Gdk::Cursor* cursor;
2482 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2484 float x_fraction = cnote->mouse_x_fraction ();
2486 if (x_fraction > 0.0 && x_fraction < 0.25) {
2487 cursor = _editor->cursors()->left_side_trim;
2490 cursor = _editor->cursors()->right_side_trim;
2494 Drag::start_grab (event, cursor);
2496 region = &cnote->region_view();
2499 temp = region->snap_to_pixel (cnote->x0 (), true);
2500 _snap_delta = temp - cnote->x0 ();
2504 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2509 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2510 if (ms.size() > 1) {
2511 /* has to be relative, may make no sense otherwise */
2515 if (!(_was_selected = cnote->selected())) {
2517 /* tertiary-click means extend selection - we'll do that on button release,
2518 so don't add it here, because otherwise we make it hard to figure
2519 out the "extend-to" range.
2522 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2525 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2528 region->note_selected (cnote, true);
2530 _editor->get_selection().clear_points();
2531 region->unique_select (cnote);
2538 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2540 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2542 _editor->begin_reversible_command (_("resize notes"));
2544 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2545 MidiRegionSelection::iterator next;
2548 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2550 mrv->begin_resizing (at_front);
2556 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2557 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2559 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2563 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2565 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2566 if (_editor->snap_mode () != SnapOff) {
2570 if (_editor->snap_mode () == SnapOff) {
2572 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2573 if (apply_snap_delta) {
2579 if (apply_snap_delta) {
2583 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2589 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2591 if (!movement_occurred) {
2592 /* no motion - select note */
2593 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2594 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2595 _editor->current_mouse_mode() == Editing::MouseDraw) {
2597 bool changed = false;
2599 if (_was_selected) {
2600 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2602 region->note_deselected (cnote);
2605 _editor->get_selection().clear_points();
2606 region->unique_select (cnote);
2610 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2611 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2613 if (!extend && !add && region->selection_size() > 1) {
2614 _editor->get_selection().clear_points();
2615 region->unique_select (cnote);
2617 } else if (extend) {
2618 region->note_selected (cnote, true, true);
2621 /* it was added during button press */
2627 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2628 _editor->commit_reversible_selection_op();
2635 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2636 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2637 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2639 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2642 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2644 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2645 if (_editor->snap_mode () != SnapOff) {
2649 if (_editor->snap_mode () == SnapOff) {
2651 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2652 if (apply_snap_delta) {
2658 if (apply_snap_delta) {
2662 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2666 _editor->commit_reversible_command ();
2670 NoteResizeDrag::aborted (bool)
2672 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2673 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2674 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2676 mrv->abort_resizing ();
2681 AVDraggingView::AVDraggingView (RegionView* v)
2684 initial_position = v->region()->position ();
2687 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2690 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2693 TrackViewList empty;
2695 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2696 std::list<RegionView*> views = rs.by_layer();
2699 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2700 RegionView* rv = (*i);
2701 if (!rv->region()->video_locked()) {
2704 if (rv->region()->locked()) {
2707 _views.push_back (AVDraggingView (rv));
2712 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2714 Drag::start_grab (event);
2715 if (_editor->session() == 0) {
2719 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2725 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2729 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2730 _max_backwards_drag = (
2731 ARDOUR_UI::instance()->video_timeline->get_duration()
2732 + ARDOUR_UI::instance()->video_timeline->get_offset()
2733 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2736 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2737 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2738 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2741 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2744 Timecode::Time timecode;
2745 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2746 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);
2747 show_verbose_cursor_text (buf);
2751 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2753 if (_editor->session() == 0) {
2756 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2760 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2764 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2765 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2767 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2768 dt = - _max_backwards_drag;
2771 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2772 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2774 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2775 RegionView* rv = i->view;
2776 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2779 rv->region()->clear_changes ();
2780 rv->region()->suspend_property_changes();
2782 rv->region()->set_position(i->initial_position + dt);
2783 rv->region_changed(ARDOUR::Properties::position);
2786 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2787 Timecode::Time timecode;
2788 Timecode::Time timediff;
2790 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2791 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2792 snprintf (buf, sizeof (buf),
2793 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2794 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2795 , _("Video Start:"),
2796 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2798 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2800 show_verbose_cursor_text (buf);
2804 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2806 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2813 if (!movement_occurred || ! _editor->session()) {
2817 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2819 _editor->begin_reversible_command (_("Move Video"));
2821 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2822 ARDOUR_UI::instance()->video_timeline->save_undo();
2823 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2824 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2826 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2827 i->view->drag_end();
2828 i->view->region()->resume_property_changes ();
2830 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2833 _editor->session()->maybe_update_session_range(
2834 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2835 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2839 _editor->commit_reversible_command ();
2843 VideoTimeLineDrag::aborted (bool)
2845 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2848 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2849 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2851 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2852 i->view->region()->resume_property_changes ();
2853 i->view->region()->set_position(i->initial_position);
2857 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2858 : RegionDrag (e, i, p, v)
2859 , _operation (StartTrim)
2860 , _preserve_fade_anchor (preserve_fade_anchor)
2861 , _jump_position_when_done (false)
2863 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2867 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2870 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2871 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2873 if (tv && tv->is_track()) {
2874 speed = tv->track()->speed();
2877 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2878 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2879 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2881 framepos_t const pf = adjusted_current_frame (event);
2882 setup_snap_delta (MusicFrame(region_start, 0));
2884 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2885 /* Move the contents of the region around without changing the region bounds */
2886 _operation = ContentsTrim;
2887 Drag::start_grab (event, _editor->cursors()->trimmer);
2889 /* These will get overridden for a point trim.*/
2890 if (pf < (region_start + region_length/2)) {
2891 /* closer to front */
2892 _operation = StartTrim;
2893 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2894 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2896 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2900 _operation = EndTrim;
2901 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2902 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2904 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2908 /* jump trim disabled for now
2909 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2910 _jump_position_when_done = true;
2914 switch (_operation) {
2916 show_verbose_cursor_time (region_start);
2919 show_verbose_cursor_duration (region_start, region_end);
2922 show_verbose_cursor_time (pf);
2926 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2927 i->view->region()->suspend_property_changes ();
2932 TrimDrag::motion (GdkEvent* event, bool first_move)
2934 RegionView* rv = _primary;
2937 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2938 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2939 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2940 frameoffset_t frame_delta = 0;
2942 if (tv && tv->is_track()) {
2943 speed = tv->track()->speed();
2945 MusicFrame adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2946 framecnt_t dt = adj_frame.frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2952 switch (_operation) {
2954 trim_type = "Region start trim";
2957 trim_type = "Region end trim";
2960 trim_type = "Region content trim";
2967 _editor->begin_reversible_command (trim_type);
2969 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2970 RegionView* rv = i->view;
2971 rv->region()->playlist()->clear_owned_changes ();
2973 if (_operation == StartTrim) {
2974 rv->trim_front_starting ();
2977 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2980 arv->temporarily_hide_envelope ();
2984 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2985 insert_result = _editor->motion_frozen_playlists.insert (pl);
2987 if (insert_result.second) {
2993 bool non_overlap_trim = false;
2995 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2996 non_overlap_trim = true;
2999 /* contstrain trim to fade length */
3000 if (_preserve_fade_anchor) {
3001 switch (_operation) {
3003 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3004 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3006 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3007 if (ar->locked()) continue;
3008 framecnt_t len = ar->fade_in()->back()->when;
3009 if (len < dt) dt = min(dt, len);
3013 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3014 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3016 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3017 if (ar->locked()) continue;
3018 framecnt_t len = ar->fade_out()->back()->when;
3019 if (len < -dt) dt = max(dt, -len);
3027 switch (_operation) {
3029 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3030 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
3031 , adj_frame.division);
3033 if (changed && _preserve_fade_anchor) {
3034 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3036 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3037 framecnt_t len = ar->fade_in()->back()->when;
3038 framecnt_t diff = ar->first_frame() - i->initial_position;
3039 framepos_t new_length = len - diff;
3040 i->anchored_fade_length = min (ar->length(), new_length);
3041 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
3042 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
3049 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3050 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, adj_frame.division);
3051 if (changed && _preserve_fade_anchor) {
3052 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3054 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3055 framecnt_t len = ar->fade_out()->back()->when;
3056 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
3057 framepos_t new_length = len + diff;
3058 i->anchored_fade_length = min (ar->length(), new_length);
3059 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
3060 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3068 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3070 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3071 i->view->move_contents (frame_delta);
3077 switch (_operation) {
3079 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3082 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3085 // show_verbose_cursor_time (frame_delta);
3091 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3093 if (movement_occurred) {
3094 motion (event, false);
3096 if (_operation == StartTrim) {
3097 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3099 /* This must happen before the region's StatefulDiffCommand is created, as it may
3100 `correct' (ahem) the region's _start from being negative to being zero. It
3101 needs to be zero in the undo record.
3103 i->view->trim_front_ending ();
3105 if (_preserve_fade_anchor) {
3106 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3108 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3109 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3110 ar->set_fade_in_length(i->anchored_fade_length);
3111 ar->set_fade_in_active(true);
3114 if (_jump_position_when_done) {
3115 i->view->region()->set_position (i->initial_position);
3118 } else if (_operation == EndTrim) {
3119 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3120 if (_preserve_fade_anchor) {
3121 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3123 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3124 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3125 ar->set_fade_out_length(i->anchored_fade_length);
3126 ar->set_fade_out_active(true);
3129 if (_jump_position_when_done) {
3130 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3135 if (!_editor->selection->selected (_primary)) {
3136 _primary->thaw_after_trim ();
3139 set<boost::shared_ptr<Playlist> > diffed_playlists;
3141 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3142 i->view->thaw_after_trim ();
3143 i->view->enable_display (true);
3145 /* Trimming one region may affect others on the playlist, so we need
3146 to get undo Commands from the whole playlist rather than just the
3147 region. Use diffed_playlists to make sure we don't diff a given
3148 playlist more than once.
3150 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3151 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3152 vector<Command*> cmds;
3154 _editor->session()->add_commands (cmds);
3155 diffed_playlists.insert (p);
3160 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3164 _editor->motion_frozen_playlists.clear ();
3165 _editor->commit_reversible_command();
3168 /* no mouse movement */
3169 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false).frame) {
3170 _editor->point_trim (event, adjusted_current_frame (event));
3174 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3175 i->view->region()->resume_property_changes ();
3180 TrimDrag::aborted (bool movement_occurred)
3182 /* Our motion method is changing model state, so use the Undo system
3183 to cancel. Perhaps not ideal, as this will leave an Undo point
3184 behind which may be slightly odd from the user's point of view.
3188 finished (&ev, true);
3190 if (movement_occurred) {
3191 _editor->session()->undo (1);
3194 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3195 i->view->region()->resume_property_changes ();
3200 TrimDrag::setup_pointer_frame_offset ()
3202 list<DraggingView>::iterator i = _views.begin ();
3203 while (i != _views.end() && i->view != _primary) {
3207 if (i == _views.end()) {
3211 switch (_operation) {
3213 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3216 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3223 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3226 , _old_snap_type (e->snap_type())
3227 , _old_snap_mode (e->snap_mode())
3230 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3231 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3233 _real_section = &_marker->meter();
3238 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3240 Drag::start_grab (event, cursor);
3241 show_verbose_cursor_time (adjusted_current_frame(event));
3245 MeterMarkerDrag::setup_pointer_frame_offset ()
3247 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3251 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3254 // create a dummy marker to catch events, then hide it.
3257 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3259 _marker = new MeterMarker (
3261 *_editor->meter_group,
3262 UIConfiguration::instance().color ("meter marker"),
3264 *new MeterSection (_marker->meter())
3267 /* use the new marker for the grab */
3268 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3271 TempoMap& map (_editor->session()->tempo_map());
3272 /* get current state */
3273 before_state = &map.get_state();
3276 _editor->begin_reversible_command (_("move meter mark"));
3278 _editor->begin_reversible_command (_("copy meter mark"));
3280 Timecode::BBT_Time bbt = _real_section->bbt();
3282 /* we can't add a meter where one currently exists */
3283 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3288 const double beat = map.beat_at_bbt (bbt);
3289 const framepos_t frame = map.frame_at_beat (beat);
3290 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3291 , beat, bbt, frame, _real_section->position_lock_style());
3292 if (!_real_section) {
3298 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3299 if (_real_section->position_lock_style() != AudioTime) {
3300 _editor->set_snap_to (SnapToBar);
3301 _editor->set_snap_mode (SnapNormal);
3305 framepos_t pf = adjusted_current_frame (event);
3307 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3308 /* never snap to music for audio locked */
3309 pf = adjusted_current_frame (event, false);
3312 _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3314 /* fake marker meeds to stay under the mouse, unlike the real one. */
3315 _marker->set_position (adjusted_current_frame (event, false));
3317 show_verbose_cursor_time (_real_section->frame());
3321 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3323 if (!movement_occurred) {
3324 if (was_double_click()) {
3325 _editor->edit_meter_marker (*_marker);
3330 /* reinstate old snap setting */
3331 _editor->set_snap_to (_old_snap_type);
3332 _editor->set_snap_mode (_old_snap_mode);
3334 TempoMap& map (_editor->session()->tempo_map());
3336 XMLNode &after = map.get_state();
3337 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3338 _editor->commit_reversible_command ();
3340 // delete the dummy marker we used for visual representation while moving.
3341 // a new visual marker will show up automatically.
3346 MeterMarkerDrag::aborted (bool moved)
3348 _marker->set_position (_marker->meter().frame ());
3350 /* reinstate old snap setting */
3351 _editor->set_snap_to (_old_snap_type);
3352 _editor->set_snap_mode (_old_snap_mode);
3354 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3355 // delete the dummy marker we used for visual representation while moving.
3356 // a new visual marker will show up automatically.
3361 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3367 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3369 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3370 _real_section = &_marker->tempo();
3371 _movable = !_real_section->initial();
3372 _grab_bpm = _real_section->note_types_per_minute();
3377 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3379 Drag::start_grab (event, cursor);
3380 if (!_real_section->active()) {
3381 show_verbose_cursor_text (_("inactive"));
3383 show_verbose_cursor_time (adjusted_current_frame (event));
3388 TempoMarkerDrag::setup_pointer_frame_offset ()
3390 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3394 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3396 if (!_real_section->active()) {
3402 // mvc drag - create a dummy marker to catch events, hide it.
3405 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3407 TempoSection section (_marker->tempo());
3409 _marker = new TempoMarker (
3411 *_editor->tempo_group,
3412 UIConfiguration::instance().color ("tempo marker"),
3414 *new TempoSection (_marker->tempo())
3417 /* use the new marker for the grab */
3418 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3421 TempoMap& map (_editor->session()->tempo_map());
3422 /* get current state */
3423 before_state = &map.get_state();
3426 _editor->begin_reversible_command (_("move tempo mark"));
3429 const Tempo tempo (_marker->tempo());
3430 const framepos_t frame = adjusted_current_frame (event) + 1;
3431 const TempoSection::Type type = _real_section->type();
3433 _editor->begin_reversible_command (_("copy tempo mark"));
3435 if (_real_section->position_lock_style() == MusicTime) {
3436 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3437 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3439 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3442 if (!_real_section) {
3450 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3451 /* use vertical movement to alter tempo .. should be log */
3452 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3454 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3456 show_verbose_cursor_text (strs.str());
3458 } else if (_movable && !_real_section->locked_to_meter()) {
3461 if (_editor->snap_musical()) {
3462 /* we can't snap to a grid that we are about to move.
3463 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3465 pf = adjusted_current_frame (event, false);
3467 pf = adjusted_current_frame (event);
3470 TempoMap& map (_editor->session()->tempo_map());
3472 /* snap to beat is 1, snap to bar is -1 (sorry) */
3473 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3475 map.gui_set_tempo_position (_real_section, pf, sub_num);
3477 show_verbose_cursor_time (_real_section->frame());
3479 _marker->set_position (adjusted_current_frame (event, false));
3483 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3485 if (!_real_section->active()) {
3488 if (!movement_occurred) {
3489 if (was_double_click()) {
3490 _editor->edit_tempo_marker (*_marker);
3495 TempoMap& map (_editor->session()->tempo_map());
3497 XMLNode &after = map.get_state();
3498 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3499 _editor->commit_reversible_command ();
3501 // delete the dummy marker we used for visual representation while moving.
3502 // a new visual marker will show up automatically.
3507 TempoMarkerDrag::aborted (bool moved)
3509 _marker->set_position (_marker->tempo().frame());
3511 TempoMap& map (_editor->session()->tempo_map());
3512 map.set_state (*before_state, Stateful::current_state_version);
3513 // delete the dummy (hidden) marker we used for events while moving.
3518 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3524 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3529 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3531 Drag::start_grab (event, cursor);
3532 TempoMap& map (_editor->session()->tempo_map());
3533 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3536 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3537 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3538 show_verbose_cursor_text (sstr.str());
3539 finished (event, false);
3543 BBTRulerDrag::setup_pointer_frame_offset ()
3545 TempoMap& map (_editor->session()->tempo_map());
3546 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3547 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3550 if (divisions > 0) {
3551 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3553 /* while it makes some sense for the user to determine the division to 'grab',
3554 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3555 and the result over steep tempo curves. Use sixteenths.
3557 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3560 _grab_qn = map.quarter_note_at_beat (beat);
3562 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3567 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3569 TempoMap& map (_editor->session()->tempo_map());
3572 /* get current state */
3573 before_state = &map.get_state();
3574 _editor->begin_reversible_command (_("stretch tempo"));
3579 if (_editor->snap_musical()) {
3580 pf = adjusted_current_frame (event, false);
3582 pf = adjusted_current_frame (event);
3585 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3586 /* adjust previous tempo to match pointer frame */
3587 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3590 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3591 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3592 show_verbose_cursor_text (sstr.str());
3596 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3598 if (!movement_occurred) {
3602 TempoMap& map (_editor->session()->tempo_map());
3604 XMLNode &after = map.get_state();
3605 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3606 _editor->commit_reversible_command ();
3610 BBTRulerDrag::aborted (bool moved)
3613 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3618 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3619 : Drag (e, &c.track_canvas_item(), false)
3624 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3627 /** Do all the things we do when dragging the playhead to make it look as though
3628 * we have located, without actually doing the locate (because that would cause
3629 * the diskstream buffers to be refilled, which is too slow).
3632 CursorDrag::fake_locate (framepos_t t)
3634 if (_editor->session () == 0) {
3638 _editor->playhead_cursor->set_position (t);
3640 Session* s = _editor->session ();
3641 if (s->timecode_transmission_suspended ()) {
3642 framepos_t const f = _editor->playhead_cursor->current_frame ();
3643 /* This is asynchronous so it will be sent "now"
3645 s->send_mmc_locate (f);
3646 /* These are synchronous and will be sent during the next
3649 s->queue_full_time_code ();
3650 s->queue_song_position_pointer ();
3653 show_verbose_cursor_time (t);
3654 _editor->UpdateAllTransportClocks (t);
3658 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3660 Drag::start_grab (event, c);
3661 setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
3663 _grab_zoom = _editor->samples_per_pixel;
3665 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3667 _editor->snap_to_with_modifier (where, event);
3668 _editor->_dragging_playhead = true;
3670 Session* s = _editor->session ();
3672 /* grab the track canvas item as well */
3674 _cursor.track_canvas_item().grab();
3677 if (_was_rolling && _stop) {
3681 if (s->is_auditioning()) {
3682 s->cancel_audition ();
3686 if (AudioEngine::instance()->connected()) {
3688 /* do this only if we're the engine is connected
3689 * because otherwise this request will never be
3690 * serviced and we'll busy wait forever. likewise,
3691 * notice if we are disconnected while waiting for the
3692 * request to be serviced.
3695 s->request_suspend_timecode_transmission ();
3696 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3697 /* twiddle our thumbs */
3702 fake_locate (where.frame - snap_delta (event->button.state));
3706 CursorDrag::motion (GdkEvent* event, bool)
3708 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3710 _editor->snap_to_with_modifier (where, event);
3712 if (where.frame != last_pointer_frame()) {
3713 fake_locate (where.frame - snap_delta (event->button.state));
3718 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3720 _editor->_dragging_playhead = false;
3722 _cursor.track_canvas_item().ungrab();
3724 if (!movement_occurred && _stop) {
3728 motion (event, false);
3730 Session* s = _editor->session ();
3732 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3733 _editor->_pending_locate_request = true;
3734 s->request_resume_timecode_transmission ();
3739 CursorDrag::aborted (bool)
3741 _cursor.track_canvas_item().ungrab();
3743 if (_editor->_dragging_playhead) {
3744 _editor->session()->request_resume_timecode_transmission ();
3745 _editor->_dragging_playhead = false;
3748 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false).frame);
3751 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3752 : RegionDrag (e, i, p, v)
3754 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3758 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3760 Drag::start_grab (event, cursor);
3762 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3763 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3764 setup_snap_delta (MusicFrame (r->position(), 0));
3766 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3770 FadeInDrag::setup_pointer_frame_offset ()
3772 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3773 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3774 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3778 FadeInDrag::motion (GdkEvent* event, bool)
3780 framecnt_t fade_length;
3782 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3783 _editor->snap_to_with_modifier (pos, event);
3785 pos.frame -= snap_delta (event->button.state);
3787 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3789 if (pos.frame < (region->position() + 64)) {
3790 fade_length = 64; // this should be a minimum defined somewhere
3791 } else if (pos.frame > region->position() + region->length() - region->fade_out()->back()->when) {
3792 fade_length = region->length() - region->fade_out()->back()->when - 1;
3794 fade_length = pos.frame - region->position();
3797 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3799 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3805 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3808 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3812 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3814 if (!movement_occurred) {
3818 framecnt_t fade_length;
3819 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3821 _editor->snap_to_with_modifier (pos, event);
3822 pos.frame -= snap_delta (event->button.state);
3824 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3826 if (pos.frame < (region->position() + 64)) {
3827 fade_length = 64; // this should be a minimum defined somewhere
3828 } else if (pos.frame >= region->position() + region->length() - region->fade_out()->back()->when) {
3829 fade_length = region->length() - region->fade_out()->back()->when - 1;
3831 fade_length = pos.frame - region->position();
3834 bool in_command = false;
3836 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3838 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3844 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3845 XMLNode &before = alist->get_state();
3847 tmp->audio_region()->set_fade_in_length (fade_length);
3848 tmp->audio_region()->set_fade_in_active (true);
3851 _editor->begin_reversible_command (_("change fade in length"));
3854 XMLNode &after = alist->get_state();
3855 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3859 _editor->commit_reversible_command ();
3864 FadeInDrag::aborted (bool)
3866 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3867 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3873 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3877 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3878 : RegionDrag (e, i, p, v)
3880 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3884 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3886 Drag::start_grab (event, cursor);
3888 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3889 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3890 setup_snap_delta (MusicFrame (r->last_frame(), 0));
3892 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3896 FadeOutDrag::setup_pointer_frame_offset ()
3898 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3899 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3900 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3904 FadeOutDrag::motion (GdkEvent* event, bool)
3906 framecnt_t fade_length;
3907 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3909 _editor->snap_to_with_modifier (pos, event);
3910 pos.frame -= snap_delta (event->button.state);
3912 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3914 if (pos.frame > (region->last_frame() - 64)) {
3915 fade_length = 64; // this should really be a minimum fade defined somewhere
3916 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
3917 fade_length = region->length() - region->fade_in()->back()->when - 1;
3919 fade_length = region->last_frame() - pos.frame;
3922 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3924 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3930 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3933 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3937 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3939 if (!movement_occurred) {
3943 framecnt_t fade_length;
3944 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3946 _editor->snap_to_with_modifier (pos, event);
3947 pos.frame -= snap_delta (event->button.state);
3949 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3951 if (pos.frame > (region->last_frame() - 64)) {
3952 fade_length = 64; // this should really be a minimum fade defined somewhere
3953 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
3954 fade_length = region->length() - region->fade_in()->back()->when - 1;
3956 fade_length = region->last_frame() - pos.frame;
3959 bool in_command = false;
3961 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3963 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3969 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3970 XMLNode &before = alist->get_state();
3972 tmp->audio_region()->set_fade_out_length (fade_length);
3973 tmp->audio_region()->set_fade_out_active (true);
3976 _editor->begin_reversible_command (_("change fade out length"));
3979 XMLNode &after = alist->get_state();
3980 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3984 _editor->commit_reversible_command ();
3989 FadeOutDrag::aborted (bool)
3991 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3992 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3998 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
4002 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4004 , _selection_changed (false)
4006 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4007 Gtk::Window* toplevel = _editor->current_toplevel();
4008 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4012 _points.push_back (ArdourCanvas::Duple (0, 0));
4014 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4017 MarkerDrag::~MarkerDrag ()
4019 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4024 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4026 location = new Location (*l);
4027 markers.push_back (m);
4032 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4034 Drag::start_grab (event, cursor);
4038 Location *location = _editor->find_location_from_marker (_marker, is_start);
4039 _editor->_dragging_edit_point = true;
4041 update_item (location);
4043 // _drag_line->show();
4044 // _line->raise_to_top();
4047 show_verbose_cursor_time (location->start());
4049 show_verbose_cursor_time (location->end());
4051 setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
4053 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4056 case Selection::Toggle:
4057 /* we toggle on the button release */
4059 case Selection::Set:
4060 if (!_editor->selection->selected (_marker)) {
4061 _editor->selection->set (_marker);
4062 _selection_changed = true;
4065 case Selection::Extend:
4067 Locations::LocationList ll;
4068 list<ArdourMarker*> to_add;
4070 _editor->selection->markers.range (s, e);
4071 s = min (_marker->position(), s);
4072 e = max (_marker->position(), e);
4075 if (e < max_framepos) {
4078 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4079 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4080 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4083 to_add.push_back (lm->start);
4086 to_add.push_back (lm->end);
4090 if (!to_add.empty()) {
4091 _editor->selection->add (to_add);
4092 _selection_changed = true;
4096 case Selection::Add:
4097 _editor->selection->add (_marker);
4098 _selection_changed = true;
4103 /* Set up copies for us to manipulate during the drag
4106 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4108 Location* l = _editor->find_location_from_marker (*i, is_start);
4115 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4117 /* range: check that the other end of the range isn't
4120 CopiedLocationInfo::iterator x;
4121 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4122 if (*(*x).location == *l) {
4126 if (x == _copied_locations.end()) {
4127 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4129 (*x).markers.push_back (*i);
4130 (*x).move_both = true;
4138 MarkerDrag::setup_pointer_frame_offset ()
4141 Location *location = _editor->find_location_from_marker (_marker, is_start);
4142 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4146 MarkerDrag::motion (GdkEvent* event, bool)
4148 framecnt_t f_delta = 0;
4150 bool move_both = false;
4151 Location *real_location;
4152 Location *copy_location = 0;
4153 framecnt_t const sd = snap_delta (event->button.state);
4155 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true).frame - sd;
4156 framepos_t next = newframe;
4158 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4162 CopiedLocationInfo::iterator x;
4164 /* find the marker we're dragging, and compute the delta */
4166 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4168 copy_location = (*x).location;
4170 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4172 /* this marker is represented by this
4173 * CopiedLocationMarkerInfo
4176 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4181 if (real_location->is_mark()) {
4182 f_delta = newframe - copy_location->start();
4186 switch (_marker->type()) {
4187 case ArdourMarker::SessionStart:
4188 case ArdourMarker::RangeStart:
4189 case ArdourMarker::LoopStart:
4190 case ArdourMarker::PunchIn:
4191 f_delta = newframe - copy_location->start();
4194 case ArdourMarker::SessionEnd:
4195 case ArdourMarker::RangeEnd:
4196 case ArdourMarker::LoopEnd:
4197 case ArdourMarker::PunchOut:
4198 f_delta = newframe - copy_location->end();
4201 /* what kind of marker is this ? */
4210 if (x == _copied_locations.end()) {
4211 /* hmm, impossible - we didn't find the dragged marker */
4215 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4217 /* now move them all */
4219 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4221 copy_location = x->location;
4223 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4227 if (real_location->locked()) {
4231 if (copy_location->is_mark()) {
4234 copy_location->set_start (copy_location->start() + f_delta, false, true, divisions);
4238 framepos_t new_start = copy_location->start() + f_delta;
4239 framepos_t new_end = copy_location->end() + f_delta;
4241 if (is_start) { // start-of-range marker
4243 if (move_both || (*x).move_both) {
4244 copy_location->set_start (new_start, false, true, divisions);
4245 copy_location->set_end (new_end, false, true, divisions);
4246 } else if (new_start < copy_location->end()) {
4247 copy_location->set_start (new_start, false, true, divisions);
4248 } else if (newframe > 0) {
4249 //_editor->snap_to (next, RoundUpAlways, true);
4250 copy_location->set_end (next, false, true, divisions);
4251 copy_location->set_start (newframe, false, true, divisions);
4254 } else { // end marker
4256 if (move_both || (*x).move_both) {
4257 copy_location->set_end (new_end, divisions);
4258 copy_location->set_start (new_start, false, true, divisions);
4259 } else if (new_end > copy_location->start()) {
4260 copy_location->set_end (new_end, false, true, divisions);
4261 } else if (newframe > 0) {
4262 //_editor->snap_to (next, RoundDownAlways, true);
4263 copy_location->set_start (next, false, true, divisions);
4264 copy_location->set_end (newframe, false, true, divisions);
4269 update_item (copy_location);
4271 /* now lookup the actual GUI items used to display this
4272 * location and move them to wherever the copy of the location
4273 * is now. This means that the logic in ARDOUR::Location is
4274 * still enforced, even though we are not (yet) modifying
4275 * the real Location itself.
4278 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4281 lm->set_position (copy_location->start(), copy_location->end());
4286 assert (!_copied_locations.empty());
4288 show_verbose_cursor_time (newframe);
4292 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4294 if (!movement_occurred) {
4296 if (was_double_click()) {
4297 _editor->rename_marker (_marker);
4301 /* just a click, do nothing but finish
4302 off the selection process
4305 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4307 case Selection::Set:
4308 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4309 _editor->selection->set (_marker);
4310 _selection_changed = true;
4314 case Selection::Toggle:
4315 /* we toggle on the button release, click only */
4316 _editor->selection->toggle (_marker);
4317 _selection_changed = true;
4321 case Selection::Extend:
4322 case Selection::Add:
4326 if (_selection_changed) {
4327 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4328 _editor->commit_reversible_selection_op();
4334 _editor->_dragging_edit_point = false;
4336 XMLNode &before = _editor->session()->locations()->get_state();
4337 bool in_command = false;
4339 MarkerSelection::iterator i;
4340 CopiedLocationInfo::iterator x;
4341 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4344 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4345 x != _copied_locations.end() && i != _editor->selection->markers.end();
4348 Location * location = _editor->find_location_from_marker (*i, is_start);
4352 if (location->locked()) {
4356 _editor->begin_reversible_command ( _("move marker") );
4359 if (location->is_mark()) {
4360 location->set_start (((*x).location)->start(), false, true, divisions);
4362 location->set (((*x).location)->start(), ((*x).location)->end(), true, divisions);
4365 if (location->is_session_range()) {
4366 _editor->session()->set_end_is_free (false);
4372 XMLNode &after = _editor->session()->locations()->get_state();
4373 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4374 _editor->commit_reversible_command ();
4379 MarkerDrag::aborted (bool movement_occurred)
4381 if (!movement_occurred) {
4385 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4387 /* move all markers to their original location */
4390 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4393 Location * location = _editor->find_location_from_marker (*m, is_start);
4396 (*m)->set_position (is_start ? location->start() : location->end());
4403 MarkerDrag::update_item (Location*)
4408 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4410 , _fixed_grab_x (0.0)
4411 , _fixed_grab_y (0.0)
4412 , _cumulative_x_drag (0.0)
4413 , _cumulative_y_drag (0.0)
4417 if (_zero_gain_fraction < 0.0) {
4418 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4421 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4423 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4429 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4431 Drag::start_grab (event, _editor->cursors()->fader);
4433 // start the grab at the center of the control point so
4434 // the point doesn't 'jump' to the mouse after the first drag
4435 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4436 _fixed_grab_y = _point->get_y();
4438 setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
4440 float const fraction = 1 - (_point->get_y() / _point->line().height());
4441 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4443 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4445 if (!_point->can_slide ()) {
4446 _x_constrained = true;
4451 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4453 double dx = _drags->current_pointer_x() - last_pointer_x();
4454 double dy = current_pointer_y() - last_pointer_y();
4455 bool need_snap = true;
4457 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4463 /* coordinate in pixels relative to the start of the region (for region-based automation)
4464 or track (for track-based automation) */
4465 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4466 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4468 // calculate zero crossing point. back off by .01 to stay on the
4469 // positive side of zero
4470 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4472 if (_x_constrained) {
4475 if (_y_constrained) {
4479 _cumulative_x_drag = cx - _fixed_grab_x;
4480 _cumulative_y_drag = cy - _fixed_grab_y;
4484 cy = min ((double) _point->line().height(), cy);
4486 // make sure we hit zero when passing through
4487 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4491 MusicFrame cx_mf (_editor->pixel_to_sample (cx) + snap_delta (event->button.state), 0);
4493 if (!_x_constrained && need_snap) {
4494 _editor->snap_to_with_modifier (cx_mf, event);
4497 cx_mf.frame -= snap_delta (event->button.state);
4498 cx_mf.frame = min (cx_mf.frame, _point->line().maximum_time() + _point->line().offset());
4500 float const fraction = 1.0 - (cy / _point->line().height());
4503 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4504 _editor->begin_reversible_command (_("automation event move"));
4505 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4507 pair<double, float> result;
4508 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
4510 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4514 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4516 if (!movement_occurred) {
4519 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4520 _editor->reset_point_selection ();
4524 _point->line().end_drag (_pushing, _final_index);
4525 _editor->commit_reversible_command ();
4530 ControlPointDrag::aborted (bool)
4532 _point->line().reset ();
4536 ControlPointDrag::active (Editing::MouseMode m)
4538 if (m == Editing::MouseDraw) {
4539 /* always active in mouse draw */
4543 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4544 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4547 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4550 , _fixed_grab_x (0.0)
4551 , _fixed_grab_y (0.0)
4552 , _cumulative_y_drag (0)
4556 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4560 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4562 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4565 _item = &_line->grab_item ();
4567 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4568 origin, and ditto for y.
4571 double mx = event->button.x;
4572 double my = event->button.y;
4574 _line->grab_item().canvas_to_item (mx, my);
4576 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4578 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4579 /* no adjacent points */
4583 Drag::start_grab (event, _editor->cursors()->fader);
4585 /* store grab start in item frame */
4586 double const bx = _line->nth (_before)->get_x();
4587 double const ax = _line->nth (_after)->get_x();
4588 double const click_ratio = (ax - mx) / (ax - bx);
4590 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4595 double fraction = 1.0 - (cy / _line->height());
4597 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4601 LineDrag::motion (GdkEvent* event, bool first_move)
4603 double dy = current_pointer_y() - last_pointer_y();
4605 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4609 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4611 _cumulative_y_drag = cy - _fixed_grab_y;
4614 cy = min ((double) _line->height(), cy);
4616 double const fraction = 1.0 - (cy / _line->height());
4620 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4622 _editor->begin_reversible_command (_("automation range move"));
4623 _line->start_drag_line (_before, _after, initial_fraction);
4626 /* we are ignoring x position for this drag, so we can just pass in anything */
4627 pair<double, float> result;
4629 result = _line->drag_motion (0, fraction, true, false, ignored);
4630 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4634 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4636 if (movement_occurred) {
4637 motion (event, false);
4638 _line->end_drag (false, 0);
4639 _editor->commit_reversible_command ();
4641 /* add a new control point on the line */
4643 AutomationTimeAxisView* atv;
4645 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4646 framepos_t where = grab_frame ();
4649 double cy = _fixed_grab_y;
4651 _line->grab_item().item_to_canvas (cx, cy);
4653 atv->add_automation_event (event, where, cy, false);
4654 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4655 AudioRegionView* arv;
4657 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4658 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4665 LineDrag::aborted (bool)
4670 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4674 _region_view_grab_x (0.0),
4675 _cumulative_x_drag (0),
4679 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4683 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4685 Drag::start_grab (event);
4687 _line = reinterpret_cast<Line*> (_item);
4690 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4692 double cx = event->button.x;
4693 double cy = event->button.y;
4695 _item->parent()->canvas_to_item (cx, cy);
4697 /* store grab start in parent frame */
4698 _region_view_grab_x = cx;
4700 _before = *(float*) _item->get_data ("position");
4702 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4704 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4708 FeatureLineDrag::motion (GdkEvent*, bool)
4710 double dx = _drags->current_pointer_x() - last_pointer_x();
4712 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4714 _cumulative_x_drag += dx;
4716 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4725 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4727 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4729 float *pos = new float;
4732 _line->set_data ("position", pos);
4738 FeatureLineDrag::finished (GdkEvent*, bool)
4740 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4741 _arv->update_transient(_before, _before);
4745 FeatureLineDrag::aborted (bool)
4750 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4752 , _vertical_only (false)
4754 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4758 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4760 Drag::start_grab (event);
4761 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4765 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4771 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4772 MusicFrame grab (grab_frame (), 0);
4774 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4775 _editor->snap_to_with_modifier (grab, event);
4777 grab.frame = raw_grab_frame ();
4780 /* base start and end on initial click position */
4782 if (pf < grab.frame) {
4790 if (current_pointer_y() < grab_y()) {
4791 y1 = current_pointer_y();
4794 y2 = current_pointer_y();
4798 if (start != end || y1 != y2) {
4800 double x1 = _editor->sample_to_pixel (start);
4801 double x2 = _editor->sample_to_pixel (end);
4802 const double min_dimension = 2.0;
4804 if (_vertical_only) {
4805 /* fixed 10 pixel width */
4809 x2 = min (x1 - min_dimension, x2);
4811 x2 = max (x1 + min_dimension, x2);
4816 y2 = min (y1 - min_dimension, y2);
4818 y2 = max (y1 + min_dimension, y2);
4821 /* translate rect into item space and set */
4823 ArdourCanvas::Rect r (x1, y1, x2, y2);
4825 /* this drag is a _trackview_only == true drag, so the y1 and
4826 * y2 (computed using current_pointer_y() and grab_y()) will be
4827 * relative to the top of the trackview group). The
4828 * rubberband rect has the same parent/scroll offset as the
4829 * the trackview group, so we can use the "r" rect directly
4830 * to set the shape of the rubberband.
4833 _editor->rubberband_rect->set (r);
4834 _editor->rubberband_rect->show();
4835 _editor->rubberband_rect->raise_to_top();
4837 show_verbose_cursor_time (pf);
4839 do_select_things (event, true);
4844 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4848 framepos_t grab = grab_frame ();
4849 framepos_t lpf = last_pointer_frame ();
4851 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4852 grab = raw_grab_frame ();
4853 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4867 if (current_pointer_y() < grab_y()) {
4868 y1 = current_pointer_y();
4871 y2 = current_pointer_y();
4875 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4879 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4881 if (movement_occurred) {
4883 motion (event, false);
4884 do_select_things (event, false);
4890 bool do_deselect = true;
4891 MidiTimeAxisView* mtv;
4893 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4895 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4896 /* nothing selected */
4897 add_midi_region (mtv, true);
4898 do_deselect = false;
4902 /* do not deselect if Primary or Tertiary (toggle-select or
4903 * extend-select are pressed.
4906 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4907 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4914 _editor->rubberband_rect->hide();
4918 RubberbandSelectDrag::aborted (bool)
4920 _editor->rubberband_rect->hide ();
4923 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4924 : RegionDrag (e, i, p, v)
4926 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4930 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4932 Drag::start_grab (event, cursor);
4934 _editor->get_selection().add (_primary);
4936 MusicFrame where (_primary->region()->position(), 0);
4937 setup_snap_delta (where);
4939 show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
4943 TimeFXDrag::motion (GdkEvent* event, bool)
4945 RegionView* rv = _primary;
4946 StreamView* cv = rv->get_time_axis_view().view ();
4947 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4948 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4949 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4950 MusicFrame pf (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4952 _editor->snap_to_with_modifier (pf, event);
4953 pf.frame -= snap_delta (event->button.state);
4955 if (pf.frame > rv->region()->position()) {
4956 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf.frame, layers, layer);
4959 show_verbose_cursor_duration (_primary->region()->position(), pf.frame, 0);
4963 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4965 /* this may have been a single click, no drag. We still want the dialog
4966 to show up in that case, so that the user can manually edit the
4967 parameters for the timestretch.
4970 float fraction = 1.0;
4972 if (movement_occurred) {
4974 motion (event, false);
4976 _primary->get_time_axis_view().hide_timestretch ();
4978 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4980 if (adjusted_frame_pos < _primary->region()->position()) {
4981 /* backwards drag of the left edge - not usable */
4985 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4987 fraction = (double) newlen / (double) _primary->region()->length();
4989 #ifndef USE_RUBBERBAND
4990 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4991 if (_primary->region()->data_type() == DataType::AUDIO) {
4992 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4997 if (!_editor->get_selection().regions.empty()) {
4998 /* primary will already be included in the selection, and edit
4999 group shared editing will propagate selection across
5000 equivalent regions, so just use the current region
5004 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5005 error << _("An error occurred while executing time stretch operation") << endmsg;
5011 TimeFXDrag::aborted (bool)
5013 _primary->get_time_axis_view().hide_timestretch ();
5016 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5019 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5023 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5025 Drag::start_grab (event);
5029 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5031 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5035 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5037 if (movement_occurred && _editor->session()) {
5038 /* make sure we stop */
5039 _editor->session()->request_transport_speed (0.0);
5044 ScrubDrag::aborted (bool)
5049 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5053 , _track_selection_at_start (e)
5054 , _time_selection_at_start (!_editor->get_selection().time.empty())
5056 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5058 if (_time_selection_at_start) {
5059 start_at_start = _editor->get_selection().time.start();
5060 end_at_start = _editor->get_selection().time.end_frame();
5065 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5067 if (_editor->session() == 0) {
5071 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5073 switch (_operation) {
5074 case CreateSelection:
5075 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5080 cursor = _editor->cursors()->selector;
5081 Drag::start_grab (event, cursor);
5084 case SelectionStartTrim:
5085 if (_editor->clicked_axisview) {
5086 _editor->clicked_axisview->order_selection_trims (_item, true);
5088 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5091 case SelectionEndTrim:
5092 if (_editor->clicked_axisview) {
5093 _editor->clicked_axisview->order_selection_trims (_item, false);
5095 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5099 Drag::start_grab (event, cursor);
5102 case SelectionExtend:
5103 Drag::start_grab (event, cursor);
5107 if (_operation == SelectionMove) {
5108 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5110 show_verbose_cursor_time (adjusted_current_frame (event));
5115 SelectionDrag::setup_pointer_frame_offset ()
5117 switch (_operation) {
5118 case CreateSelection:
5119 _pointer_frame_offset = 0;
5122 case SelectionStartTrim:
5124 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5127 case SelectionEndTrim:
5128 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5131 case SelectionExtend:
5137 SelectionDrag::motion (GdkEvent* event, bool first_move)
5139 framepos_t start = 0;
5141 framecnt_t length = 0;
5142 framecnt_t distance = 0;
5143 MusicFrame start_mf (0, 0);
5144 framepos_t const pending_position = adjusted_current_frame (event);
5146 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5151 _track_selection_at_start = _editor->selection->tracks;
5154 switch (_operation) {
5155 case CreateSelection:
5157 MusicFrame grab (grab_frame (), 0);
5159 grab.frame = adjusted_current_frame (event, false);
5160 if (grab.frame < pending_position) {
5161 _editor->snap_to (grab, RoundDownMaybe);
5163 _editor->snap_to (grab, RoundUpMaybe);
5167 if (pending_position < grab.frame) {
5168 start = pending_position;
5171 end = pending_position;
5175 /* first drag: Either add to the selection
5176 or create a new selection
5183 /* adding to the selection */
5184 _editor->set_selected_track_as_side_effect (Selection::Add);
5185 _editor->clicked_selection = _editor->selection->add (start, end);
5192 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5193 _editor->set_selected_track_as_side_effect (Selection::Set);
5196 _editor->clicked_selection = _editor->selection->set (start, end);
5200 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5201 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5202 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5204 _editor->selection->add (atest);
5208 /* select all tracks within the rectangle that we've marked out so far */
5209 TrackViewList new_selection;
5210 TrackViewList& all_tracks (_editor->track_views);
5212 ArdourCanvas::Coord const top = grab_y();
5213 ArdourCanvas::Coord const bottom = current_pointer_y();
5215 if (top >= 0 && bottom >= 0) {
5217 //first, find the tracks that are covered in the y range selection
5218 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5219 if ((*i)->covered_by_y_range (top, bottom)) {
5220 new_selection.push_back (*i);
5224 //now compare our list with the current selection, and add as necessary
5225 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5226 TrackViewList tracks_to_add;
5227 TrackViewList tracks_to_remove;
5230 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
5231 if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
5232 tracks_to_remove.push_back (*i);
5237 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5238 if (!_editor->selection->tracks.contains (*i)) {
5239 tracks_to_add.push_back (*i);
5243 _editor->selection->add (tracks_to_add);
5245 if (!tracks_to_remove.empty()) {
5246 _editor->selection->remove (tracks_to_remove);
5252 case SelectionStartTrim:
5254 end = _editor->selection->time[_editor->clicked_selection].end;
5256 if (pending_position > end) {
5259 start = pending_position;
5263 case SelectionEndTrim:
5265 start = _editor->selection->time[_editor->clicked_selection].start;
5267 if (pending_position < start) {
5270 end = pending_position;
5277 start = _editor->selection->time[_editor->clicked_selection].start;
5278 end = _editor->selection->time[_editor->clicked_selection].end;
5280 length = end - start;
5281 distance = pending_position - start;
5282 start = pending_position;
5284 start_mf.frame = start;
5285 _editor->snap_to (start_mf);
5287 end = start_mf.frame + length;
5291 case SelectionExtend:
5296 switch (_operation) {
5298 if (_time_selection_at_start) {
5299 _editor->selection->move_time (distance);
5303 _editor->selection->replace (_editor->clicked_selection, start, end);
5307 if (_operation == SelectionMove) {
5308 show_verbose_cursor_time(start);
5310 show_verbose_cursor_time(pending_position);
5315 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5317 Session* s = _editor->session();
5319 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5320 if (movement_occurred) {
5321 motion (event, false);
5322 /* XXX this is not object-oriented programming at all. ick */
5323 if (_editor->selection->time.consolidate()) {
5324 _editor->selection->TimeChanged ();
5327 /* XXX what if its a music time selection? */
5329 if (s->get_play_range() && s->transport_rolling()) {
5330 s->request_play_range (&_editor->selection->time, true);
5331 } else if (!s->config.get_external_sync()) {
5332 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5333 s->request_locate (_editor->get_selection().time.start());
5337 if (_editor->get_selection().time.length() != 0) {
5338 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5340 s->clear_range_selection ();
5345 /* just a click, no pointer movement.
5348 if (was_double_click()) {
5349 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5350 _editor->temporal_zoom_selection (Both);
5355 if (_operation == SelectionExtend) {
5356 if (_time_selection_at_start) {
5357 framepos_t pos = adjusted_current_frame (event, false);
5358 framepos_t start = min (pos, start_at_start);
5359 framepos_t end = max (pos, end_at_start);
5360 _editor->selection->set (start, end);
5363 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5364 if (_editor->clicked_selection) {
5365 _editor->selection->remove (_editor->clicked_selection);
5368 if (!_editor->clicked_selection) {
5369 _editor->selection->clear_time();
5374 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5375 _editor->selection->set (_editor->clicked_axisview);
5378 if (s && s->get_play_range () && s->transport_rolling()) {
5379 s->request_stop (false, false);
5384 _editor->stop_canvas_autoscroll ();
5385 _editor->clicked_selection = 0;
5386 _editor->commit_reversible_selection_op ();
5390 SelectionDrag::aborted (bool)
5395 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5396 : Drag (e, i, false),
5400 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5402 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5403 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5404 physical_screen_height (_editor->current_toplevel()->get_window())));
5405 _drag_rect->hide ();
5407 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5408 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5411 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5413 /* normal canvas items will be cleaned up when their parent group is deleted. But
5414 this item is created as the child of a long-lived parent group, and so we
5415 need to explicitly delete it.
5421 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5423 if (_editor->session() == 0) {
5427 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5429 if (!_editor->temp_location) {
5430 _editor->temp_location = new Location (*_editor->session());
5433 switch (_operation) {
5434 case CreateSkipMarker:
5435 case CreateRangeMarker:
5436 case CreateTransportMarker:
5437 case CreateCDMarker:
5439 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5444 cursor = _editor->cursors()->selector;
5448 Drag::start_grab (event, cursor);
5450 show_verbose_cursor_time (adjusted_current_frame (event));
5454 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5456 framepos_t start = 0;
5458 ArdourCanvas::Rectangle *crect;
5460 switch (_operation) {
5461 case CreateSkipMarker:
5462 crect = _editor->range_bar_drag_rect;
5464 case CreateRangeMarker:
5465 crect = _editor->range_bar_drag_rect;
5467 case CreateTransportMarker:
5468 crect = _editor->transport_bar_drag_rect;
5470 case CreateCDMarker:
5471 crect = _editor->cd_marker_bar_drag_rect;
5474 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5479 framepos_t const pf = adjusted_current_frame (event);
5481 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5482 MusicFrame grab (grab_frame (), 0);
5483 _editor->snap_to (grab);
5485 if (pf < grab_frame()) {
5493 /* first drag: Either add to the selection
5494 or create a new selection.
5499 _editor->temp_location->set (start, end);
5503 update_item (_editor->temp_location);
5505 //_drag_rect->raise_to_top();
5511 _editor->temp_location->set (start, end);
5513 double x1 = _editor->sample_to_pixel (start);
5514 double x2 = _editor->sample_to_pixel (end);
5518 update_item (_editor->temp_location);
5521 show_verbose_cursor_time (pf);
5526 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5528 Location * newloc = 0;
5532 if (movement_occurred) {
5533 motion (event, false);
5536 switch (_operation) {
5537 case CreateSkipMarker:
5538 case CreateRangeMarker:
5539 case CreateCDMarker:
5541 XMLNode &before = _editor->session()->locations()->get_state();
5542 if (_operation == CreateSkipMarker) {
5543 _editor->begin_reversible_command (_("new skip marker"));
5544 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5545 flags = Location::IsRangeMarker | Location::IsSkip;
5546 _editor->range_bar_drag_rect->hide();
5547 } else if (_operation == CreateCDMarker) {
5548 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5549 _editor->begin_reversible_command (_("new CD marker"));
5550 flags = Location::IsRangeMarker | Location::IsCDMarker;
5551 _editor->cd_marker_bar_drag_rect->hide();
5553 _editor->begin_reversible_command (_("new skip marker"));
5554 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5555 flags = Location::IsRangeMarker;
5556 _editor->range_bar_drag_rect->hide();
5558 newloc = new Location (
5559 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5560 , _editor->get_grid_music_divisions (event->button.state));
5562 _editor->session()->locations()->add (newloc, true);
5563 XMLNode &after = _editor->session()->locations()->get_state();
5564 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5565 _editor->commit_reversible_command ();
5569 case CreateTransportMarker:
5570 // popup menu to pick loop or punch
5571 _editor->new_transport_marker_context_menu (&event->button, _item);
5577 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5579 if (_operation == CreateTransportMarker) {
5581 /* didn't drag, so just locate */
5583 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5585 } else if (_operation == CreateCDMarker) {
5587 /* didn't drag, but mark is already created so do
5590 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5595 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5597 if (end == max_framepos) {
5598 end = _editor->session()->current_end_frame ();
5601 if (start == max_framepos) {
5602 start = _editor->session()->current_start_frame ();
5605 switch (_editor->mouse_mode) {
5607 /* find the two markers on either side and then make the selection from it */
5608 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5612 /* find the two markers on either side of the click and make the range out of it */
5613 _editor->selection->set (start, end);
5622 _editor->stop_canvas_autoscroll ();
5626 RangeMarkerBarDrag::aborted (bool movement_occurred)
5628 if (movement_occurred) {
5629 _drag_rect->hide ();
5634 RangeMarkerBarDrag::update_item (Location* location)
5636 double const x1 = _editor->sample_to_pixel (location->start());
5637 double const x2 = _editor->sample_to_pixel (location->end());
5639 _drag_rect->set_x0 (x1);
5640 _drag_rect->set_x1 (x2);
5643 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5645 , _cumulative_dx (0)
5646 , _cumulative_dy (0)
5648 , _was_selected (false)
5651 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5653 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5655 _region = &_primary->region_view ();
5656 _note_height = _region->midi_stream_view()->note_height ();
5660 NoteDrag::setup_pointer_frame_offset ()
5662 _pointer_frame_offset = raw_grab_frame()
5663 - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
5667 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5669 Drag::start_grab (event);
5671 if (ArdourKeyboard::indicates_copy (event->button.state)) {
5677 setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
5679 if (!(_was_selected = _primary->selected())) {
5681 /* tertiary-click means extend selection - we'll do that on button release,
5682 so don't add it here, because otherwise we make it hard to figure
5683 out the "extend-to" range.
5686 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5689 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5692 _region->note_selected (_primary, true);
5694 _editor->get_selection().clear_points();
5695 _region->unique_select (_primary);
5701 /** @return Current total drag x change in quarter notes */
5703 NoteDrag::total_dx (GdkEvent * event) const
5705 if (_x_constrained) {
5709 TempoMap& map (_editor->session()->tempo_map());
5712 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5714 /* primary note time */
5715 frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
5717 /* primary note time in quarter notes */
5718 double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
5720 /* new time of the primary note in session frames */
5721 frameoffset_t st = n + dx + snap_delta (event->button.state);
5723 /* possibly snap and return corresponding delta in quarter notes */
5724 MusicFrame snap (st, 0);
5725 _editor->snap_to_with_modifier (snap, event);
5726 double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
5728 /* prevent the earliest note being dragged earlier than the region's start position */
5729 if (_earliest + ret < _region->midi_region()->start_beats()) {
5730 ret -= (_earliest + ret) - _region->midi_region()->start_beats();
5736 /** @return Current total drag y change in note number */
5738 NoteDrag::total_dy () const
5740 if (_y_constrained) {
5744 double const y = _region->midi_view()->y_position ();
5745 /* new current note */
5746 uint8_t n = _region->y_to_note (current_pointer_y () - y);
5748 MidiStreamView* msv = _region->midi_stream_view ();
5749 n = max (msv->lowest_note(), n);
5750 n = min (msv->highest_note(), n);
5751 /* and work out delta */
5752 return n - _region->y_to_note (grab_y() - y);
5756 NoteDrag::motion (GdkEvent * event, bool first_move)
5759 _earliest = _region->earliest_in_selection().to_double();
5761 /* make copies of all the selected notes */
5762 _primary = _region->copy_selection (_primary);
5766 /* Total change in x and y since the start of the drag */
5767 double const dx_qn = total_dx (event);
5768 int8_t const dy = total_dy ();
5770 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5771 double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
5772 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5775 _cumulative_dx = dx_qn;
5776 _cumulative_dy += tdy;
5778 int8_t note_delta = total_dy();
5782 _region->move_copies (dx_qn, tdy, note_delta);
5784 _region->move_selection (dx_qn, tdy, note_delta);
5787 /* the new note value may be the same as the old one, but we
5788 * don't know what that means because the selection may have
5789 * involved more than one note and we might be doing something
5790 * odd with them. so show the note value anyway, always.
5793 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5795 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5801 NoteDrag::finished (GdkEvent* ev, bool moved)
5804 /* no motion - select note */
5806 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5807 _editor->current_mouse_mode() == Editing::MouseDraw) {
5809 bool changed = false;
5811 if (_was_selected) {
5812 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5814 _region->note_deselected (_primary);
5817 _editor->get_selection().clear_points();
5818 _region->unique_select (_primary);
5822 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5823 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5825 if (!extend && !add && _region->selection_size() > 1) {
5826 _editor->get_selection().clear_points();
5827 _region->unique_select (_primary);
5829 } else if (extend) {
5830 _region->note_selected (_primary, true, true);
5833 /* it was added during button press */
5840 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5841 _editor->commit_reversible_selection_op();
5845 _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
5850 NoteDrag::aborted (bool)
5855 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5856 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5857 : Drag (editor, atv->base_item ())
5859 , _y_origin (atv->y_position())
5860 , _nothing_to_drag (false)
5862 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5863 setup (atv->lines ());
5866 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5867 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5868 : Drag (editor, rv->get_canvas_group ())
5870 , _y_origin (rv->get_time_axis_view().y_position())
5871 , _nothing_to_drag (false)
5874 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5876 list<boost::shared_ptr<AutomationLine> > lines;
5878 AudioRegionView* audio_view;
5879 AutomationRegionView* automation_view;
5880 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5881 lines.push_back (audio_view->get_gain_line ());
5882 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5883 lines.push_back (automation_view->line ());
5886 error << _("Automation range drag created for invalid region type") << endmsg;
5892 /** @param lines AutomationLines to drag.
5893 * @param offset Offset from the session start to the points in the AutomationLines.
5896 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5898 /* find the lines that overlap the ranges being dragged */
5899 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5900 while (i != lines.end ()) {
5901 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5904 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5906 /* check this range against all the AudioRanges that we are using */
5907 list<AudioRange>::const_iterator k = _ranges.begin ();
5908 while (k != _ranges.end()) {
5909 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5915 /* add it to our list if it overlaps at all */
5916 if (k != _ranges.end()) {
5921 _lines.push_back (n);
5927 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5931 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5933 return 1.0 - ((global_y - _y_origin) / line->height());
5937 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5939 const double v = list->eval(x);
5940 return _integral ? rint(v) : v;
5944 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5946 Drag::start_grab (event, cursor);
5948 /* Get line states before we start changing things */
5949 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5950 i->state = &i->line->get_state ();
5951 i->original_fraction = y_fraction (i->line, current_pointer_y());
5954 if (_ranges.empty()) {
5956 /* No selected time ranges: drag all points */
5957 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5958 uint32_t const N = i->line->npoints ();
5959 for (uint32_t j = 0; j < N; ++j) {
5960 i->points.push_back (i->line->nth (j));
5966 if (_nothing_to_drag) {
5972 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5974 if (_nothing_to_drag && !first_move) {
5979 _editor->begin_reversible_command (_("automation range move"));
5981 if (!_ranges.empty()) {
5983 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5985 framecnt_t const half = (i->start + i->end) / 2;
5987 /* find the line that this audio range starts in */
5988 list<Line>::iterator j = _lines.begin();
5989 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5993 if (j != _lines.end()) {
5994 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5996 /* j is the line that this audio range starts in; fade into it;
5997 64 samples length plucked out of thin air.
6000 framepos_t a = i->start + 64;
6005 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
6006 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
6008 XMLNode &before = the_list->get_state();
6009 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6010 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6012 if (add_p || add_q) {
6013 _editor->session()->add_command (
6014 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6018 /* same thing for the end */
6021 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6025 if (j != _lines.end()) {
6026 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6028 /* j is the line that this audio range starts in; fade out of it;
6029 64 samples length plucked out of thin air.
6032 framepos_t b = i->end - 64;
6037 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6038 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6040 XMLNode &before = the_list->get_state();
6041 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6042 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6044 if (add_p || add_q) {
6045 _editor->session()->add_command (
6046 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6051 _nothing_to_drag = true;
6053 /* Find all the points that should be dragged and put them in the relevant
6054 points lists in the Line structs.
6057 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6059 uint32_t const N = i->line->npoints ();
6060 for (uint32_t j = 0; j < N; ++j) {
6062 /* here's a control point on this line */
6063 ControlPoint* p = i->line->nth (j);
6064 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6066 /* see if it's inside a range */
6067 list<AudioRange>::const_iterator k = _ranges.begin ();
6068 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6072 if (k != _ranges.end()) {
6073 /* dragging this point */
6074 _nothing_to_drag = false;
6075 i->points.push_back (p);
6081 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6082 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6086 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6087 float const f = y_fraction (l->line, current_pointer_y());
6088 /* we are ignoring x position for this drag, so we can just pass in anything */
6089 pair<double, float> result;
6091 result = l->line->drag_motion (0, f, true, false, ignored);
6092 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6097 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6099 if (_nothing_to_drag || !motion_occurred) {
6103 motion (event, false);
6104 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6105 i->line->end_drag (false, 0);
6108 _editor->commit_reversible_command ();
6112 AutomationRangeDrag::aborted (bool)
6114 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6119 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6121 , initial_time_axis_view (itav)
6123 /* note that time_axis_view may be null if the regionview was created
6124 * as part of a copy operation.
6126 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6127 layer = v->region()->layer ();
6128 initial_y = v->get_canvas_group()->position().y;
6129 initial_playlist = v->region()->playlist ();
6130 initial_position = v->region()->position ();
6131 initial_end = v->region()->position () + v->region()->length ();
6134 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6135 : Drag (e, i->canvas_item ())
6138 , _cumulative_dx (0)
6140 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6141 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6146 PatchChangeDrag::motion (GdkEvent* ev, bool)
6148 framepos_t f = adjusted_current_frame (ev);
6149 boost::shared_ptr<Region> r = _region_view->region ();
6150 f = max (f, r->position ());
6151 f = min (f, r->last_frame ());
6153 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6154 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6155 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6156 _cumulative_dx = dxu;
6160 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6162 if (!movement_occurred) {
6163 if (was_double_click()) {
6164 _region_view->edit_patch_change (_patch_change);
6169 boost::shared_ptr<Region> r (_region_view->region ());
6170 framepos_t f = adjusted_current_frame (ev);
6171 f = max (f, r->position ());
6172 f = min (f, r->last_frame ());
6174 _region_view->move_patch_change (
6176 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6181 PatchChangeDrag::aborted (bool)
6183 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6187 PatchChangeDrag::setup_pointer_frame_offset ()
6189 boost::shared_ptr<Region> region = _region_view->region ();
6190 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6193 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6194 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6201 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6203 _region_view->update_drag_selection (
6205 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6209 MidiRubberbandSelectDrag::deselect_things ()
6214 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6215 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6218 _vertical_only = true;
6222 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6224 double const y = _region_view->midi_view()->y_position ();
6226 y1 = max (0.0, y1 - y);
6227 y2 = max (0.0, y2 - y);
6229 _region_view->update_vertical_drag_selection (
6232 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6237 MidiVerticalSelectDrag::deselect_things ()
6242 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6243 : RubberbandSelectDrag (e, i)
6249 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6251 if (drag_in_progress) {
6252 /* We just want to select things at the end of the drag, not during it */
6256 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6258 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6260 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6262 _editor->commit_reversible_selection_op ();
6266 EditorRubberbandSelectDrag::deselect_things ()
6268 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6270 _editor->selection->clear_tracks();
6271 _editor->selection->clear_regions();
6272 _editor->selection->clear_points ();
6273 _editor->selection->clear_lines ();
6274 _editor->selection->clear_midi_notes ();
6276 _editor->commit_reversible_selection_op();
6279 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6284 _note[0] = _note[1] = 0;
6287 NoteCreateDrag::~NoteCreateDrag ()
6293 NoteCreateDrag::grid_frames (framepos_t t) const
6296 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6297 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6299 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6300 - _region_view->region_beats_to_region_frames (t_beats);
6304 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6306 Drag::start_grab (event, cursor);
6308 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6309 TempoMap& map (_editor->session()->tempo_map());
6311 const framepos_t pf = _drags->current_pointer_frame ();
6312 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6314 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6316 double eqaf = map.exact_qn_at_frame (pf, divisions);
6318 if (divisions != 0) {
6320 const double qaf = map.quarter_note_at_frame (pf);
6322 /* Hack so that we always snap to the note that we are over, instead of snapping
6323 to the next one if we're more than halfway through the one we're over.
6326 const double rem = eqaf - qaf;
6328 eqaf -= grid_beats.to_double();
6332 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6333 /* minimum initial length is grid beats */
6334 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6336 double const x0 = _editor->sample_to_pixel (_note[0]);
6337 double const x1 = _editor->sample_to_pixel (_note[1]);
6338 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6340 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6341 _drag_rect->set_outline_all ();
6342 _drag_rect->set_outline_color (0xffffff99);
6343 _drag_rect->set_fill_color (0xffffff66);
6347 NoteCreateDrag::motion (GdkEvent* event, bool)
6349 TempoMap& map (_editor->session()->tempo_map());
6350 const framepos_t pf = _drags->current_pointer_frame ();
6351 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6352 double eqaf = map.exact_qn_at_frame (pf, divisions);
6354 if (divisions != 0) {
6356 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6358 const double qaf = map.quarter_note_at_frame (pf);
6359 /* Hack so that we always snap to the note that we are over, instead of snapping
6360 to the next one if we're more than halfway through the one we're over.
6363 const double rem = eqaf - qaf;
6365 eqaf -= grid_beats.to_double();
6368 eqaf += grid_beats.to_double();
6370 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6372 double const x0 = _editor->sample_to_pixel (_note[0]);
6373 double const x1 = _editor->sample_to_pixel (_note[1]);
6374 _drag_rect->set_x0 (std::min(x0, x1));
6375 _drag_rect->set_x1 (std::max(x0, x1));
6379 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6381 /* we create a note even if there was no movement */
6382 framepos_t const start = min (_note[0], _note[1]);
6383 framepos_t const start_sess_rel = start + _region_view->region()->position();
6384 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6385 framecnt_t const g = grid_frames (start);
6387 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6391 TempoMap& map (_editor->session()->tempo_map());
6392 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6393 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6395 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6399 NoteCreateDrag::y_to_region (double y) const
6402 _region_view->get_canvas_group()->canvas_to_item (x, y);
6407 NoteCreateDrag::aborted (bool)
6412 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6420 HitCreateDrag::~HitCreateDrag ()
6425 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6427 Drag::start_grab (event, cursor);
6429 TempoMap& map (_editor->session()->tempo_map());
6431 const framepos_t pf = _drags->current_pointer_frame ();
6432 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6434 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6436 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6438 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6442 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6443 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6445 Evoral::Beats length = _region_view->get_grid_beats (pf);
6447 _region_view->create_note_at (start, y, length, event->button.state, false);
6454 HitCreateDrag::motion (GdkEvent* event, bool)
6456 TempoMap& map (_editor->session()->tempo_map());
6458 const framepos_t pf = _drags->current_pointer_frame ();
6459 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6461 if (divisions == 0) {
6465 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6466 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6467 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6469 if (_last_pos == start && y == _last_y) {
6473 Evoral::Beats length = _region_view->get_grid_beats (pf);
6475 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6476 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6480 _region_view->create_note_at (start, y, length, event->button.state, false);
6487 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6493 HitCreateDrag::y_to_region (double y) const
6496 _region_view->get_canvas_group()->canvas_to_item (x, y);
6501 HitCreateDrag::aborted (bool)
6506 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6511 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6515 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6517 Drag::start_grab (event, cursor);
6521 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6527 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6530 distance = _drags->current_pointer_x() - grab_x();
6531 len = ar->fade_in()->back()->when;
6533 distance = grab_x() - _drags->current_pointer_x();
6534 len = ar->fade_out()->back()->when;
6537 /* how long should it be ? */
6539 new_length = len + _editor->pixel_to_sample (distance);
6541 /* now check with the region that this is legal */
6543 new_length = ar->verify_xfade_bounds (new_length, start);
6546 arv->reset_fade_in_shape_width (ar, new_length);
6548 arv->reset_fade_out_shape_width (ar, new_length);
6553 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6559 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6562 distance = _drags->current_pointer_x() - grab_x();
6563 len = ar->fade_in()->back()->when;
6565 distance = grab_x() - _drags->current_pointer_x();
6566 len = ar->fade_out()->back()->when;
6569 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6571 _editor->begin_reversible_command ("xfade trim");
6572 ar->playlist()->clear_owned_changes ();
6575 ar->set_fade_in_length (new_length);
6577 ar->set_fade_out_length (new_length);
6580 /* Adjusting the xfade may affect other regions in the playlist, so we need
6581 to get undo Commands from the whole playlist rather than just the
6585 vector<Command*> cmds;
6586 ar->playlist()->rdiff (cmds);
6587 _editor->session()->add_commands (cmds);
6588 _editor->commit_reversible_command ();
6593 CrossfadeEdgeDrag::aborted (bool)
6596 // arv->redraw_start_xfade ();
6598 // arv->redraw_end_xfade ();
6602 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6603 : Drag (e, item, true)
6604 , line (new EditorCursor (*e))
6606 line->set_position (pos);
6608 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6611 RegionCutDrag::~RegionCutDrag ()
6617 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6619 Drag::start_grab (event, c);
6620 motion (event, false);
6624 RegionCutDrag::motion (GdkEvent* event, bool)
6626 MusicFrame pos (_drags->current_pointer_frame(), 0);
6627 _editor->snap_to_with_modifier (pos, event);
6629 line->set_position (pos.frame);
6633 RegionCutDrag::finished (GdkEvent* event, bool)
6635 _editor->get_track_canvas()->canvas()->re_enter();
6638 MusicFrame pos (_drags->current_pointer_frame(), 0);
6639 _editor->snap_to_with_modifier (pos, event);
6642 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos.frame);
6648 _editor->split_regions_at (pos, rs, false);
6652 RegionCutDrag::aborted (bool)
6656 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6657 : Drag (e, item, true)
6662 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6664 Drag::start_grab (event, c);
6666 framepos_t where = _editor->canvas_event_sample(event);
6668 _editor->_dragging_playhead = true;
6670 _editor->playhead_cursor->set_position (where);
6674 RulerZoomDrag::motion (GdkEvent* event, bool)
6676 framepos_t where = _editor->canvas_event_sample(event);
6678 _editor->playhead_cursor->set_position (where);
6680 const double movement_limit = 20.0;
6681 const double scale = 1.08;
6682 const double y_delta = last_pointer_y() - current_pointer_y();
6684 if (y_delta > 0 && y_delta < movement_limit) {
6685 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6686 } else if (y_delta < 0 && y_delta > -movement_limit) {
6687 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6692 RulerZoomDrag::finished (GdkEvent*, bool)
6694 _editor->_dragging_playhead = false;
6696 Session* s = _editor->session ();
6698 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6699 _editor->_pending_locate_request = true;
6705 RulerZoomDrag::aborted (bool)