2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/profile.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/session.h"
43 #include "ardour/session_playlists.h"
45 #include "canvas/canvas.h"
46 #include "canvas/scroll_group.h"
51 #include "audio_region_view.h"
52 #include "automation_region_view.h"
53 #include "midi_region_view.h"
54 #include "ardour_ui.h"
55 #include "gui_thread.h"
56 #include "control_point.h"
57 #include "region_gain_line.h"
58 #include "editor_drag.h"
59 #include "audio_time_axis.h"
60 #include "midi_time_axis.h"
61 #include "selection.h"
62 #include "midi_selection.h"
63 #include "automation_time_axis.h"
65 #include "editor_cursors.h"
66 #include "mouse_cursors.h"
67 #include "note_base.h"
68 #include "patch_change.h"
69 #include "ui_config.h"
70 #include "verbose_cursor.h"
73 using namespace ARDOUR;
76 using namespace Gtkmm2ext;
77 using namespace Editing;
78 using namespace ArdourCanvas;
80 using Gtkmm2ext::Keyboard;
82 double ControlPointDrag::_zero_gain_fraction = -1.0;
84 DragManager::DragManager (Editor* e)
87 , _current_pointer_x (0.0)
88 , _current_pointer_y (0.0)
89 , _current_pointer_frame (0)
90 , _old_follow_playhead (false)
94 DragManager::~DragManager ()
99 /** Call abort for each active drag */
101 DragManager::abort ()
105 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
110 if (!_drags.empty ()) {
111 _editor->set_follow_playhead (_old_follow_playhead, false);
115 _editor->abort_reversible_command();
121 DragManager::add (Drag* d)
123 d->set_manager (this);
124 _drags.push_back (d);
128 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
130 d->set_manager (this);
131 _drags.push_back (d);
136 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
138 /* Prevent follow playhead during the drag to be nice to the user */
139 _old_follow_playhead = _editor->follow_playhead ();
140 _editor->set_follow_playhead (false);
142 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
144 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
145 (*i)->start_grab (e, c);
149 /** Call end_grab for each active drag.
150 * @return true if any drag reported movement having occurred.
153 DragManager::end_grab (GdkEvent* e)
158 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
159 bool const t = (*i)->end_grab (e);
170 _editor->set_follow_playhead (_old_follow_playhead, false);
176 DragManager::mark_double_click ()
178 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
179 (*i)->set_double_click (true);
184 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
188 /* calling this implies that we expect the event to have canvas
191 * Can we guarantee that this is true?
194 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
196 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
197 bool const t = (*i)->motion_handler (e, from_autoscroll);
198 /* run all handlers; return true if at least one of them
199 returns true (indicating that the event has been handled).
211 DragManager::have_item (ArdourCanvas::Item* i) const
213 list<Drag*>::const_iterator j = _drags.begin ();
214 while (j != _drags.end() && (*j)->item () != i) {
218 return j != _drags.end ();
221 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
225 , _pointer_frame_offset (0)
226 , _x_constrained (false)
227 , _y_constrained (false)
228 , _was_rolling (false)
229 , _trackview_only (trackview_only)
230 , _move_threshold_passed (false)
231 , _starting_point_passed (false)
232 , _initially_vertical (false)
233 , _was_double_click (false)
236 , _last_pointer_x (0.0)
237 , _last_pointer_y (0.0)
238 , _raw_grab_frame (0)
240 , _last_pointer_frame (0)
242 , _snap_delta_music (0.0)
243 , _constraint_pressed (false)
249 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
255 _cursor_ctx = CursorContext::create (*_editor, cursor);
257 _cursor_ctx->change (cursor);
264 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
267 /* we set up x/y dragging constraints on first move */
268 _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
270 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
272 setup_pointer_frame_offset ();
273 _grab_frame = adjusted_frame (_raw_grab_frame, event).frame;
274 _last_pointer_frame = _grab_frame;
275 _last_pointer_x = _grab_x;
277 if (_trackview_only) {
278 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
281 _last_pointer_y = _grab_y;
285 if (!_editor->cursors()->is_invalid (cursor)) {
286 /* CAIROCANVAS need a variant here that passes *cursor */
287 _cursor_ctx = CursorContext::create (*_editor, cursor);
290 if (_editor->session() && _editor->session()->transport_rolling()) {
293 _was_rolling = false;
296 switch (_editor->snap_type()) {
297 case SnapToRegionStart:
298 case SnapToRegionEnd:
299 case SnapToRegionSync:
300 case SnapToRegionBoundary:
301 _editor->build_region_boundary_cache ();
308 /** Call to end a drag `successfully'. Ungrabs item and calls
309 * subclass' finished() method.
311 * @param event GDK event, or 0.
312 * @return true if some movement occurred, otherwise false.
315 Drag::end_grab (GdkEvent* event)
317 _editor->stop_canvas_autoscroll ();
321 finished (event, _move_threshold_passed);
323 _editor->verbose_cursor()->hide ();
326 return _move_threshold_passed;
330 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
332 MusicFrame pos (0, 0);
334 if (f > _pointer_frame_offset) {
335 pos.frame = f - _pointer_frame_offset;
339 _editor->snap_to_with_modifier (pos, event);
346 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
348 return adjusted_frame (_drags->current_pointer_frame (), event, snap).frame;
352 Drag::snap_delta (guint state) const
354 if (ArdourKeyboard::indicates_snap_delta (state)) {
361 Drag::snap_delta_music (guint state) const
363 if (ArdourKeyboard::indicates_snap_delta (state)) {
364 return _snap_delta_music;
371 Drag::current_pointer_x() const
373 return _drags->current_pointer_x ();
377 Drag::current_pointer_y () const
379 if (!_trackview_only) {
380 return _drags->current_pointer_y ();
383 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
387 Drag::setup_snap_delta (MusicFrame pos)
389 TempoMap& map (_editor->session()->tempo_map());
390 MusicFrame snap (pos);
391 _editor->snap_to (snap, ARDOUR::RoundNearest, false, true);
392 _snap_delta = snap.frame - pos.frame;
394 _snap_delta_music = 0.0;
396 if (_snap_delta != 0) {
397 _snap_delta_music = map.exact_qn_at_frame (snap.frame, snap.division) - map.exact_qn_at_frame (pos.frame, pos.division);
402 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
404 /* check to see if we have moved in any way that matters since the last motion event */
405 if (_move_threshold_passed &&
406 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
407 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
411 pair<framecnt_t, int> const threshold = move_threshold ();
413 bool const old_move_threshold_passed = _move_threshold_passed;
415 if (!_move_threshold_passed) {
417 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
418 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
420 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
423 if (active (_editor->mouse_mode) && _move_threshold_passed) {
425 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
427 if (old_move_threshold_passed != _move_threshold_passed) {
431 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
432 _initially_vertical = true;
434 _initially_vertical = false;
436 /** check constraints for this drag.
437 * Note that the current convention is to use "contains" for
438 * key modifiers during motion and "equals" when initiating a drag.
439 * In this case we haven't moved yet, so "equals" applies here.
441 if (Config->get_edit_mode() != Lock) {
442 if (event->motion.state & Gdk::BUTTON2_MASK) {
443 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
444 if (_constraint_pressed) {
445 _x_constrained = false;
446 _y_constrained = true;
448 _x_constrained = true;
449 _y_constrained = false;
451 } else if (_constraint_pressed) {
452 // if dragging normally, the motion is constrained to the first direction of movement.
453 if (_initially_vertical) {
454 _x_constrained = true;
455 _y_constrained = false;
457 _x_constrained = false;
458 _y_constrained = true;
462 if (event->button.state & Gdk::BUTTON2_MASK) {
463 _x_constrained = false;
465 _x_constrained = true;
467 _y_constrained = false;
471 if (!from_autoscroll) {
472 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
475 if (!_editor->autoscroll_active() || from_autoscroll) {
478 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
480 motion (event, first_move && !_starting_point_passed);
482 if (first_move && !_starting_point_passed) {
483 _starting_point_passed = true;
486 _last_pointer_x = _drags->current_pointer_x ();
487 _last_pointer_y = current_pointer_y ();
488 _last_pointer_frame = adjusted_current_frame (event, false);
498 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
506 aborted (_move_threshold_passed);
508 _editor->stop_canvas_autoscroll ();
509 _editor->verbose_cursor()->hide ();
513 Drag::show_verbose_cursor_time (framepos_t frame)
515 _editor->verbose_cursor()->set_time (frame);
516 _editor->verbose_cursor()->show ();
520 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
522 _editor->verbose_cursor()->set_duration (start, end);
523 _editor->verbose_cursor()->show ();
527 Drag::show_verbose_cursor_text (string const & text)
529 _editor->verbose_cursor()->set (text);
530 _editor->verbose_cursor()->show ();
533 boost::shared_ptr<Region>
534 Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
536 if (_editor->session()) {
537 const TempoMap& map (_editor->session()->tempo_map());
538 framecnt_t pos = grab_frame();
539 /* not that the frame rate used here can be affected by pull up/down which
542 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
543 return view->add_region (grab_frame(), len, commit);
546 return boost::shared_ptr<Region>();
549 struct PresentationInfoTimeAxisViewSorter {
550 bool operator() (TimeAxisView* a, TimeAxisView* b) {
551 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
555 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
560 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
562 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
563 as some of the regions we are dragging may be on such tracks.
566 TrackViewList track_views = _editor->track_views;
567 track_views.sort (PresentationInfoTimeAxisViewSorter ());
569 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
570 _time_axis_views.push_back (*i);
572 TimeAxisView::Children children_list = (*i)->get_child_list ();
573 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
574 _time_axis_views.push_back (j->get());
578 /* the list of views can be empty at this point if this is a region list-insert drag
581 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
582 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
585 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
589 RegionDrag::region_going_away (RegionView* v)
591 list<DraggingView>::iterator i = _views.begin ();
592 while (i != _views.end() && i->view != v) {
596 if (i != _views.end()) {
601 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
602 * or -1 if it is not found.
605 RegionDrag::find_time_axis_view (TimeAxisView* t) const
608 int const N = _time_axis_views.size ();
609 while (i < N && _time_axis_views[i] != t) {
613 if (_time_axis_views[i] != t) {
620 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
621 : RegionDrag (e, i, p, v)
623 , _ignore_video_lock (false)
624 , _last_position (0, 0)
626 , _last_pointer_time_axis_view (0)
627 , _last_pointer_layer (0)
632 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
636 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
638 Drag::start_grab (event, cursor);
639 setup_snap_delta (_last_position);
641 show_verbose_cursor_time (_last_position.frame);
643 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
645 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
646 assert(_last_pointer_time_axis_view >= 0);
647 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
650 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
651 _ignore_video_lock = true;
655 /* cross track dragging seems broken here. disabled for now. */
656 _y_constrained = true;
661 RegionMotionDrag::compute_x_delta (GdkEvent const * event, MusicFrame* pending_region_position)
663 /* compute the amount of pointer motion in frames, and where
664 the region would be if we moved it by that much.
666 if (_x_constrained) {
667 *pending_region_position = _last_position;
671 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
673 framecnt_t sync_offset;
676 sync_offset = _primary->region()->sync_offset (sync_dir);
678 /* we don't handle a sync point that lies before zero.
680 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position->frame >= sync_offset)) {
682 framecnt_t const sd = snap_delta (event->button.state);
683 MusicFrame sync_snap (pending_region_position->frame + (sync_dir * sync_offset) + sd, 0);
684 _editor->snap_to_with_modifier (sync_snap, event);
685 if (sync_offset == 0 && sd == 0) {
686 *pending_region_position = sync_snap;
688 pending_region_position->set (_primary->region()->adjust_to_sync (sync_snap.frame) - sd, 0);
691 *pending_region_position = _last_position;
694 if (pending_region_position->frame > max_framepos - _primary->region()->length()) {
695 *pending_region_position = _last_position;
700 bool const x_move_allowed = !_x_constrained;
702 if ((pending_region_position->frame != _last_position.frame) && x_move_allowed) {
704 /* x movement since last time (in pixels) */
705 dx = _editor->sample_to_pixel_unrounded (pending_region_position->frame - _last_position.frame);
707 /* total x movement */
708 framecnt_t total_dx = _editor->pixel_to_sample (_total_x_delta + dx);
710 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
711 frameoffset_t const off = i->view->region()->position() + total_dx;
713 dx = dx - _editor->sample_to_pixel_unrounded (off);
714 *pending_region_position = MusicFrame (pending_region_position->frame - off, 0);
724 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
730 const int tavsize = _time_axis_views.size();
731 const int dt = delta > 0 ? +1 : -1;
733 int target = start + delta - skip;
735 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
736 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
738 while (current >= 0 && current != target) {
740 if (current < 0 && dt < 0) {
743 if (current >= tavsize && dt > 0) {
746 if (current < 0 || current >= tavsize) {
750 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
751 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
755 if (distance_only && current == start + delta) {
763 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
765 if (_y_constrained) {
769 const int tavsize = _time_axis_views.size();
770 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
771 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
772 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
774 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
775 /* already in the drop zone */
776 if (delta_track >= 0) {
777 /* downward motion - OK if others are still not in the dropzone */
786 } else if (n >= tavsize) {
787 /* downward motion into drop zone. That's fine. */
791 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
792 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
793 /* not a track, or the wrong type */
797 double const l = i->layer + delta_layer;
799 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
800 mode to allow the user to place a region below another on layer 0.
802 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
803 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
804 If it has, the layers will be munged later anyway, so it's ok.
810 /* all regions being dragged are ok with this change */
814 struct DraggingViewSorter {
815 bool operator() (const DraggingView& a, const DraggingView& b) {
816 return a.time_axis_view < b.time_axis_view;
821 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
823 double delta_layer = 0;
824 int delta_time_axis_view = 0;
825 int current_pointer_time_axis_view = -1;
827 assert (!_views.empty ());
829 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
831 /* Find the TimeAxisView that the pointer is now over */
832 const double cur_y = current_pointer_y ();
833 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
834 TimeAxisView* tv = r.first;
836 if (!tv && cur_y < 0) {
837 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
841 /* find drop-zone y-position */
842 Coord last_track_bottom_edge;
843 last_track_bottom_edge = 0;
844 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
845 if (!(*t)->hidden()) {
846 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
851 if (tv && tv->view()) {
852 /* the mouse is over a track */
853 double layer = r.second;
855 if (first_move && tv->view()->layer_display() == Stacked) {
856 tv->view()->set_layer_display (Expanded);
859 /* Here's the current pointer position in terms of time axis view and layer */
860 current_pointer_time_axis_view = find_time_axis_view (tv);
861 assert(current_pointer_time_axis_view >= 0);
863 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
865 /* Work out the change in y */
867 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
868 if (!rtv || !rtv->is_track()) {
869 /* ignore non-tracks early on. we can't move any regions on them */
870 } else if (_last_pointer_time_axis_view < 0) {
871 /* Was in the drop-zone, now over a track.
872 * Hence it must be an upward move (from the bottom)
874 * track_index is still -1, so delta must be set to
875 * move up the correct number of tracks from the bottom.
877 * This is necessary because steps may be skipped if
878 * the bottom-most track is not a valid target and/or
879 * if there are hidden tracks at the bottom.
880 * Hence the initial offset (_ddropzone) as well as the
881 * last valid pointer position (_pdropzone) need to be
882 * taken into account.
884 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
886 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
889 /* TODO needs adjustment per DraggingView,
891 * e.g. select one region on the top-layer of a track
892 * and one region which is at the bottom-layer of another track
895 * Indicated drop-zones and layering is wrong.
896 * and may infer additional layers on the target-track
897 * (depending how many layers the original track had).
899 * Or select two regions (different layers) on a same track,
900 * move across a non-layer track.. -> layering info is lost.
901 * on drop either of the regions may be on top.
903 * Proposed solution: screw it :) well,
904 * don't use delta_layer, use an absolute value
905 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
906 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
907 * 3) iterate over all DraggingView, find the one that is over the track with most layers
908 * 4) proportionally scale layer to layers available on target
910 delta_layer = current_pointer_layer - _last_pointer_layer;
913 /* for automation lanes, there is a TimeAxisView but no ->view()
914 * if (!tv) -> dropzone
916 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
917 /* Moving into the drop-zone.. */
918 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
919 /* delta_time_axis_view may not be sufficient to move into the DZ
920 * the mouse may enter it, but it may not be a valid move due to
923 * -> remember the delta needed to move into the dropzone
925 _ddropzone = delta_time_axis_view;
926 /* ..but subtract hidden tracks (or routes) at the bottom.
927 * we silently move mover them
929 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
930 - _time_axis_views.size();
932 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
933 /* move around inside the zone.
934 * This allows to move further down until all regions are in the zone.
936 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
937 assert(ptr_y >= last_track_bottom_edge);
938 assert(_ddropzone > 0);
940 /* calculate mouse position in 'tracks' below last track. */
941 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
942 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
944 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
946 delta_time_axis_view = dzpos - _pdropzone;
947 } else if (dzpos < _pdropzone && _ndropzone > 0) {
948 // move up inside the DZ
949 delta_time_axis_view = dzpos - _pdropzone;
953 /* Work out the change in x */
954 TempoMap& tmap = _editor->session()->tempo_map();
955 MusicFrame pending_region_position (0, 0);
956 double const x_delta = compute_x_delta (event, &pending_region_position);
958 double const last_pos_qn = tmap.exact_qn_at_frame (_last_position.frame, _last_position.division);
959 double const qn_delta = tmap.exact_qn_at_frame (pending_region_position.frame, pending_region_position.division) - last_pos_qn;
961 _last_position = pending_region_position;
963 /* calculate hidden tracks in current y-axis delta */
965 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
966 /* The mouse is more than one track below the dropzone.
967 * distance calculation is not needed (and would not work, either
968 * because the dropzone is "packed").
970 * Except when [partially] moving regions out of dropzone in a large step.
971 * (the mouse may or may not remain in the DZ)
972 * Hidden tracks at the bottom of the TAV need to be skipped.
974 * This also handles the case if the mouse entered the DZ
975 * in a large step (exessive delta), either due to fast-movement,
976 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
978 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
979 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
981 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
982 -_time_axis_views.size() - dt;
985 else if (_last_pointer_time_axis_view < 0) {
986 /* Moving out of the zone. Check for hidden tracks at the bottom. */
987 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
988 -_time_axis_views.size() - delta_time_axis_view;
990 /* calculate hidden tracks that are skipped by the pointer movement */
991 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
992 - _last_pointer_time_axis_view
993 - delta_time_axis_view;
996 /* Verify change in y */
997 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
998 /* this y movement is not allowed, so do no y movement this time */
999 delta_time_axis_view = 0;
1004 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
1005 /* haven't reached next snap point, and we're not switching
1006 trackviews nor layers. nothing to do.
1011 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
1012 PlaylistDropzoneMap playlist_dropzone_map;
1013 _ndropzone = 0; // number of elements currently in the dropzone
1016 /* sort views by time_axis.
1017 * This retains track order in the dropzone, regardless
1018 * of actual selection order
1020 _views.sort (DraggingViewSorter());
1022 /* count number of distinct tracks of all regions
1023 * being dragged, used for dropzone.
1025 int prev_track = -1;
1026 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1027 if (i->time_axis_view != prev_track) {
1028 prev_track = i->time_axis_view;
1034 _views.back().time_axis_view -
1035 _views.front().time_axis_view;
1037 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1038 - _views.back().time_axis_view;
1040 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1044 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1046 RegionView* rv = i->view;
1051 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1058 /* reparent the regionview into a group above all
1062 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1063 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1064 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1065 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1066 /* move the item so that it continues to appear at the
1067 same location now that its parent has changed.
1069 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1072 /* If we have moved tracks, we'll fudge the layer delta so that the
1073 region gets moved back onto layer 0 on its new track; this avoids
1074 confusion when dragging regions from non-zero layers onto different
1077 double this_delta_layer = delta_layer;
1078 if (delta_time_axis_view != 0) {
1079 this_delta_layer = - i->layer;
1082 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1084 int track_index = i->time_axis_view + this_delta_time_axis_view;
1085 assert(track_index >= 0);
1087 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1088 /* Track is in the Dropzone */
1090 i->time_axis_view = track_index;
1091 assert(i->time_axis_view >= (int) _time_axis_views.size());
1094 double yposition = 0;
1095 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1096 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1099 /* store index of each new playlist as a negative count, starting at -1 */
1101 if (pdz == playlist_dropzone_map.end()) {
1102 /* compute where this new track (which doesn't exist yet) will live
1105 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1107 /* How high is this region view ? */
1109 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1110 ArdourCanvas::Rect bbox;
1113 bbox = obbox.get ();
1116 last_track_bottom_edge += bbox.height();
1118 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1121 yposition = pdz->second;
1124 /* values are zero or negative, hence the use of min() */
1125 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1128 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1130 mrv->apply_note_range (60, 71, true);
1134 /* The TimeAxisView that this region is now over */
1135 TimeAxisView* current_tv = _time_axis_views[track_index];
1137 /* Ensure it is moved from stacked -> expanded if appropriate */
1138 if (current_tv->view()->layer_display() == Stacked) {
1139 current_tv->view()->set_layer_display (Expanded);
1142 /* We're only allowed to go -ve in layer on Expanded views */
1143 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1144 this_delta_layer = - i->layer;
1148 rv->set_height (current_tv->view()->child_height ());
1150 /* Update show/hidden status as the region view may have come from a hidden track,
1151 or have moved to one.
1153 if (current_tv->hidden ()) {
1154 rv->get_canvas_group()->hide ();
1156 rv->get_canvas_group()->show ();
1159 /* Update the DraggingView */
1160 i->time_axis_view = track_index;
1161 i->layer += this_delta_layer;
1164 _editor->mouse_brush_insert_region (rv, pending_region_position.frame);
1168 /* Get the y coordinate of the top of the track that this region is now over */
1169 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1171 /* And adjust for the layer that it should be on */
1172 StreamView* cv = current_tv->view ();
1173 switch (cv->layer_display ()) {
1177 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1180 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1184 /* need to get the parent of the regionview
1185 * canvas group and get its position in
1186 * equivalent coordinate space as the trackview
1187 * we are now dragging over.
1190 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1194 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1196 MidiStreamView* msv;
1197 if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
1198 mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
1203 /* Now move the region view */
1204 if (rv->region()->position_lock_style() == MusicTime) {
1205 double const last_qn = tmap.quarter_note_at_frame (rv->get_position());
1206 framepos_t const x_pos_music = tmap.frame_at_quarter_note (last_qn + qn_delta);
1208 rv->set_position (x_pos_music, 0);
1209 rv->move (0, y_delta);
1211 rv->move (x_delta, y_delta);
1214 } /* foreach region */
1216 _total_x_delta += x_delta;
1218 if (x_delta != 0 && !_brushing) {
1219 show_verbose_cursor_time (_last_position.frame);
1222 /* keep track of pointer movement */
1224 /* the pointer is currently over a time axis view */
1226 if (_last_pointer_time_axis_view < 0) {
1227 /* last motion event was not over a time axis view
1228 * or last y-movement out of the dropzone was not valid
1231 if (delta_time_axis_view < 0) {
1232 /* in the drop zone, moving up */
1234 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1235 * We do not use negative _last_pointer_time_axis_view because
1236 * the dropzone is "packed" (the actual track offset is ignored)
1238 * As opposed to the actual number
1239 * of elements in the dropzone (_ndropzone)
1240 * _pdropzone is not constrained. This is necessary
1241 * to allow moving multiple regions with y-distance
1244 * There can be 0 elements in the dropzone,
1245 * even though the drag-pointer is inside the DZ.
1248 * [ Audio-track, Midi-track, Audio-track, DZ ]
1249 * move regions from both audio tracks at the same time into the
1250 * DZ by grabbing the region in the bottom track.
1252 assert(current_pointer_time_axis_view >= 0);
1253 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1257 /* only move out of the zone if the movement is OK */
1258 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1259 assert(delta_time_axis_view < 0);
1260 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1261 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1262 * the current position can be calculated as follows:
1264 // a well placed oofus attack can still throw this off.
1265 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1266 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1269 /* last motion event was also over a time axis view */
1270 _last_pointer_time_axis_view += delta_time_axis_view;
1271 assert(_last_pointer_time_axis_view >= 0);
1276 /* the pointer is not over a time axis view */
1277 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1278 _pdropzone += delta_time_axis_view - delta_skip;
1279 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1282 _last_pointer_layer += delta_layer;
1286 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1288 if (_copy && first_move) {
1289 if (_x_constrained && !_brushing) {
1290 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1291 } else if (!_brushing) {
1292 _editor->begin_reversible_command (Operations::region_copy);
1293 } else if (_brushing) {
1294 _editor->begin_reversible_command (Operations::drag_region_brush);
1296 /* duplicate the regionview(s) and region(s) */
1298 list<DraggingView> new_regionviews;
1300 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1302 RegionView* rv = i->view;
1303 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1304 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1306 const boost::shared_ptr<const Region> original = rv->region();
1307 boost::shared_ptr<Region> region_copy;
1309 region_copy = RegionFactory::create (original, true);
1311 /* need to set this so that the drop zone code can work. This doesn't
1312 actually put the region into the playlist, but just sets a weak pointer
1315 region_copy->set_playlist (original->playlist());
1319 boost::shared_ptr<AudioRegion> audioregion_copy
1320 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1322 nrv = new AudioRegionView (*arv, audioregion_copy);
1324 boost::shared_ptr<MidiRegion> midiregion_copy
1325 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1326 nrv = new MidiRegionView (*mrv, midiregion_copy);
1331 nrv->get_canvas_group()->show ();
1332 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1334 /* swap _primary to the copy */
1336 if (rv == _primary) {
1340 /* ..and deselect the one we copied */
1342 rv->set_selected (false);
1345 if (!new_regionviews.empty()) {
1347 /* reflect the fact that we are dragging the copies */
1349 _views = new_regionviews;
1351 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1354 } else if (!_copy && first_move) {
1355 if (_x_constrained && !_brushing) {
1356 _editor->begin_reversible_command (_("fixed time region drag"));
1357 } else if (!_brushing) {
1358 _editor->begin_reversible_command (Operations::region_drag);
1359 } else if (_brushing) {
1360 _editor->begin_reversible_command (Operations::drag_region_brush);
1363 RegionMotionDrag::motion (event, first_move);
1367 RegionMotionDrag::finished (GdkEvent *, bool)
1369 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1370 if (!(*i)->view()) {
1374 if ((*i)->view()->layer_display() == Expanded) {
1375 (*i)->view()->set_layer_display (Stacked);
1381 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1383 RegionMotionDrag::finished (ev, movement_occurred);
1385 if (!movement_occurred) {
1389 if (was_double_click() && !_views.empty()) {
1390 DraggingView dv = _views.front();
1391 _editor->edit_region (dv.view);
1397 assert (!_views.empty ());
1399 /* We might have hidden region views so that they weren't visible during the drag
1400 (when they have been reparented). Now everything can be shown again, as region
1401 views are back in their track parent groups.
1403 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1404 i->view->get_canvas_group()->show ();
1407 bool const changed_position = (_last_position.frame != _primary->region()->position());
1408 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1432 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1434 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1437 TimeAxisView* tav = 0;
1439 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1440 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1441 uint32_t output_chan = region->n_channels();
1442 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1443 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1445 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1446 tav =_editor->time_axis_view_from_stripable (audio_tracks.front());
1448 ChanCount one_midi_port (DataType::MIDI, 1);
1449 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1450 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port,
1451 Config->get_strict_io () || Profile->get_mixbus (),
1452 boost::shared_ptr<ARDOUR::PluginInfo>(),
1453 (ARDOUR::Plugin::PresetRecord*) 0,
1454 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1455 tav = _editor->time_axis_view_from_stripable (midi_tracks.front());
1459 tav->set_height (original->current_height());
1462 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1465 return dynamic_cast<RouteTimeAxisView*> (tav);
1469 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, MusicFrame last_position, int32_t const ev_state)
1471 RegionSelection new_views;
1472 PlaylistSet modified_playlists;
1473 RouteTimeAxisView* new_time_axis_view = 0;
1474 framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1476 TempoMap& tmap (_editor->session()->tempo_map());
1477 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1478 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1481 /* all changes were made during motion event handlers */
1483 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1487 _editor->commit_reversible_command ();
1491 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1492 PlaylistMapping playlist_mapping;
1494 /* insert the regions into their new playlists */
1495 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1497 RouteTimeAxisView* dest_rtv = 0;
1499 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1503 MusicFrame where (0, 0);
1504 double quarter_note;
1506 if (changed_position && !_x_constrained) {
1507 where.set (i->view->region()->position() - drag_delta, 0);
1508 quarter_note = i->view->region()->quarter_note() - qn_delta;
1510 /* region has not moved - divisor will not affect musical pos */
1511 where.set (i->view->region()->position(), 0);
1512 quarter_note = i->view->region()->quarter_note();
1515 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1516 /* dragged to drop zone */
1518 PlaylistMapping::iterator pm;
1520 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1521 /* first region from this original playlist: create a new track */
1522 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1523 if(!new_time_axis_view) {
1527 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1528 dest_rtv = new_time_axis_view;
1530 /* we already created a new track for regions from this playlist, use it */
1531 dest_rtv = pm->second;
1534 /* destination time axis view is the one we dragged to */
1535 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1538 if (dest_rtv != 0) {
1539 RegionView* new_view;
1540 if (i->view == _primary && !_x_constrained) {
1541 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, last_position, last_pos_qn,
1542 modified_playlists, true);
1544 if (i->view->region()->position_lock_style() == AudioTime) {
1545 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1546 modified_playlists);
1548 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1549 modified_playlists, true);
1553 if (new_view != 0) {
1554 new_views.push_back (new_view);
1558 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1559 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1562 list<DraggingView>::const_iterator next = i;
1568 /* If we've created new regions either by copying or moving
1569 to a new track, we want to replace the old selection with the new ones
1572 if (new_views.size() > 0) {
1573 _editor->selection->set (new_views);
1576 /* write commands for the accumulated diffs for all our modified playlists */
1577 add_stateful_diff_commands_for_playlists (modified_playlists);
1579 _editor->commit_reversible_command ();
1583 RegionMoveDrag::finished_no_copy (
1584 bool const changed_position,
1585 bool const changed_tracks,
1586 MusicFrame last_position,
1587 int32_t const ev_state
1590 RegionSelection new_views;
1591 PlaylistSet modified_playlists;
1592 PlaylistSet frozen_playlists;
1593 set<RouteTimeAxisView*> views_to_update;
1594 RouteTimeAxisView* new_time_axis_view = 0;
1595 framecnt_t const drag_delta = _primary->region()->position() - last_position.frame;
1597 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1598 PlaylistMapping playlist_mapping;
1600 TempoMap& tmap (_editor->session()->tempo_map());
1601 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1602 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1604 std::set<boost::shared_ptr<const Region> > uniq;
1605 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1607 RegionView* rv = i->view;
1608 RouteTimeAxisView* dest_rtv = 0;
1610 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1615 if (uniq.find (rv->region()) != uniq.end()) {
1616 /* prevent duplicate moves when selecting regions from shared playlists */
1620 uniq.insert(rv->region());
1622 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1623 /* dragged to drop zone */
1625 PlaylistMapping::iterator pm;
1627 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1628 /* first region from this original playlist: create a new track */
1629 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1630 if(!new_time_axis_view) { // New track creation failed
1634 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1635 dest_rtv = new_time_axis_view;
1637 /* we already created a new track for regions from this playlist, use it */
1638 dest_rtv = pm->second;
1642 /* destination time axis view is the one we dragged to */
1643 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1648 double const dest_layer = i->layer;
1650 views_to_update.insert (dest_rtv);
1652 MusicFrame where (0, 0);
1653 double quarter_note;
1655 if (changed_position && !_x_constrained) {
1656 where.set (rv->region()->position() - drag_delta, 0);
1657 quarter_note = i->view->region()->quarter_note() - qn_delta;
1659 where.set (rv->region()->position(), 0);
1660 quarter_note = i->view->region()->quarter_note();
1663 if (changed_tracks) {
1665 /* insert into new playlist */
1666 RegionView* new_view;
1667 if (rv == _primary && !_x_constrained) {
1668 new_view = insert_region_into_playlist (
1669 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, last_position, last_pos_qn,
1670 modified_playlists, true
1673 if (rv->region()->position_lock_style() == AudioTime) {
1675 new_view = insert_region_into_playlist (
1676 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1680 new_view = insert_region_into_playlist (
1681 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1682 modified_playlists, true
1687 if (new_view == 0) {
1692 new_views.push_back (new_view);
1694 /* remove from old playlist */
1696 /* the region that used to be in the old playlist is not
1697 moved to the new one - we use a copy of it. as a result,
1698 any existing editor for the region should no longer be
1701 rv->hide_region_editor();
1704 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1708 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1710 /* this movement may result in a crossfade being modified, or a layering change,
1711 so we need to get undo data from the playlist as well as the region.
1714 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1716 playlist->clear_changes ();
1719 rv->region()->clear_changes ();
1722 motion on the same track. plonk the previously reparented region
1723 back to its original canvas group (its streamview).
1724 No need to do anything for copies as they are fake regions which will be deleted.
1727 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1728 rv->get_canvas_group()->set_y_position (i->initial_y);
1731 /* just change the model */
1732 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1733 playlist->set_layer (rv->region(), dest_layer);
1736 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1738 r = frozen_playlists.insert (playlist);
1741 playlist->freeze ();
1743 if (rv == _primary) {
1744 rv->region()->set_position (where.frame, last_position.division);
1746 if (rv->region()->position_lock_style() == AudioTime) {
1747 /* move by frame offset */
1748 rv->region()->set_position (where.frame, 0);
1750 /* move by music offset */
1751 rv->region()->set_position_music (rv->region()->quarter_note() - qn_delta);
1754 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1757 if (changed_tracks) {
1759 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1760 was selected in all of them, then removing it from a playlist will have removed all
1761 trace of it from _views (i.e. there were N regions selected, we removed 1,
1762 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1763 corresponding regionview, and _views is now empty).
1765 This could have invalidated any and all iterators into _views.
1767 The heuristic we use here is: if the region selection is empty, break out of the loop
1768 here. if the region selection is not empty, then restart the loop because we know that
1769 we must have removed at least the region(view) we've just been working on as well as any
1770 that we processed on previous iterations.
1772 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1773 we can just iterate.
1777 if (_views.empty()) {
1788 /* If we've created new regions either by copying or moving
1789 to a new track, we want to replace the old selection with the new ones
1792 if (new_views.size() > 0) {
1793 _editor->selection->set (new_views);
1796 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1800 /* write commands for the accumulated diffs for all our modified playlists */
1801 add_stateful_diff_commands_for_playlists (modified_playlists);
1802 /* applies to _brushing */
1803 _editor->commit_reversible_command ();
1805 /* We have futzed with the layering of canvas items on our streamviews.
1806 If any region changed layer, this will have resulted in the stream
1807 views being asked to set up their region views, and all will be well.
1808 If not, we might now have badly-ordered region views. Ask the StreamViews
1809 involved to sort themselves out, just in case.
1812 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1813 (*i)->view()->playlist_layered ((*i)->track ());
1817 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1818 * @param region Region to remove.
1819 * @param playlist playlist To remove from.
1820 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1821 * that clear_changes () is only called once per playlist.
1824 RegionMoveDrag::remove_region_from_playlist (
1825 boost::shared_ptr<Region> region,
1826 boost::shared_ptr<Playlist> playlist,
1827 PlaylistSet& modified_playlists
1830 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1833 playlist->clear_changes ();
1836 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1840 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1841 * clearing the playlist's diff history first if necessary.
1842 * @param region Region to insert.
1843 * @param dest_rtv Destination RouteTimeAxisView.
1844 * @param dest_layer Destination layer.
1845 * @param where Destination position.
1846 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1847 * that clear_changes () is only called once per playlist.
1848 * @return New RegionView, or 0 if no insert was performed.
1851 RegionMoveDrag::insert_region_into_playlist (
1852 boost::shared_ptr<Region> region,
1853 RouteTimeAxisView* dest_rtv,
1856 double quarter_note,
1857 PlaylistSet& modified_playlists,
1861 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1862 if (!dest_playlist) {
1866 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1867 _new_region_view = 0;
1868 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1870 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1871 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1873 dest_playlist->clear_changes ();
1876 dest_playlist->add_region (region, where.frame, 1.0, false, where.division, quarter_note, true);
1878 dest_playlist->add_region (region, where.frame, 1.0, false, where.division);
1881 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1882 dest_playlist->set_layer (region, dest_layer);
1887 assert (_new_region_view);
1889 return _new_region_view;
1893 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1895 _new_region_view = rv;
1899 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1901 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1902 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1904 _editor->session()->add_command (c);
1913 RegionMoveDrag::aborted (bool movement_occurred)
1917 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1918 list<DraggingView>::const_iterator next = i;
1927 RegionMotionDrag::aborted (movement_occurred);
1932 RegionMotionDrag::aborted (bool)
1934 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1936 StreamView* sview = (*i)->view();
1939 if (sview->layer_display() == Expanded) {
1940 sview->set_layer_display (Stacked);
1945 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1946 RegionView* rv = i->view;
1947 TimeAxisView* tv = &(rv->get_time_axis_view ());
1948 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1950 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1951 rv->get_canvas_group()->set_y_position (0);
1953 rv->move (-_total_x_delta, 0);
1954 rv->set_height (rtv->view()->child_height ());
1958 /** @param b true to brush, otherwise false.
1959 * @param c true to make copies of the regions being moved, otherwise false.
1961 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1962 : RegionMotionDrag (e, i, p, v, b)
1964 , _new_region_view (0)
1966 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1969 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1970 if (rtv && rtv->is_track()) {
1971 speed = rtv->track()->speed ();
1974 _last_position = MusicFrame (static_cast<framepos_t> (_primary->region()->position() / speed), 0);
1978 RegionMoveDrag::setup_pointer_frame_offset ()
1980 _pointer_frame_offset = raw_grab_frame() - _last_position.frame;
1983 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1984 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1986 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1988 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1989 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1991 _primary = v->view()->create_region_view (r, false, false);
1993 _primary->get_canvas_group()->show ();
1994 _primary->set_position (pos, 0);
1995 _views.push_back (DraggingView (_primary, this, v));
1997 _last_position = MusicFrame (pos, 0);
1999 _item = _primary->get_canvas_group ();
2003 RegionInsertDrag::finished (GdkEvent * event, bool)
2005 int pos = _views.front().time_axis_view;
2006 assert(pos >= 0 && pos < (int)_time_axis_views.size());
2008 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
2010 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
2011 _primary->get_canvas_group()->set_y_position (0);
2013 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
2015 _editor->begin_reversible_command (Operations::insert_region);
2016 playlist->clear_changes ();
2017 _editor->snap_to_with_modifier (_last_position, event);
2019 playlist->add_region (_primary->region (), _last_position.frame, 1.0, false, _last_position.division);
2021 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
2022 if (Config->get_edit_mode() == Ripple) {
2023 playlist->ripple (_last_position.frame, _primary->region()->length(), _primary->region());
2026 _editor->session()->add_command (new StatefulDiffCommand (playlist));
2027 _editor->commit_reversible_command ();
2035 RegionInsertDrag::aborted (bool)
2042 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2043 : RegionMoveDrag (e, i, p, v, false, false)
2045 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
2048 struct RegionSelectionByPosition {
2049 bool operator() (RegionView*a, RegionView* b) {
2050 return a->region()->position () < b->region()->position();
2055 RegionSpliceDrag::motion (GdkEvent* event, bool)
2057 /* Which trackview is this ? */
2059 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2060 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2062 /* The region motion is only processed if the pointer is over
2066 if (!tv || !tv->is_track()) {
2067 /* To make sure we hide the verbose canvas cursor when the mouse is
2068 not held over an audio track.
2070 _editor->verbose_cursor()->hide ();
2073 _editor->verbose_cursor()->show ();
2078 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2084 RegionSelection copy;
2085 _editor->selection->regions.by_position(copy);
2087 framepos_t const pf = adjusted_current_frame (event);
2089 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2091 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2097 boost::shared_ptr<Playlist> playlist;
2099 if ((playlist = atv->playlist()) == 0) {
2103 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2108 if (pf < (*i)->region()->last_frame() + 1) {
2112 if (pf > (*i)->region()->first_frame()) {
2118 playlist->shuffle ((*i)->region(), dir);
2123 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2125 RegionMoveDrag::finished (event, movement_occurred);
2129 RegionSpliceDrag::aborted (bool)
2139 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2142 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2144 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2145 RegionSelection to_ripple;
2146 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2147 if ((*i)->position() >= where) {
2148 to_ripple.push_back (rtv->view()->find_view(*i));
2152 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2153 if (!exclude.contains (*i)) {
2154 // the selection has already been added to _views
2156 if (drag_in_progress) {
2157 // do the same things that RegionMotionDrag::motion does when
2158 // first_move is true, for the region views that we're adding
2159 // to _views this time
2162 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2163 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2164 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2165 rvg->reparent (_editor->_drag_motion_group);
2167 // we only need to move in the y direction
2168 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2173 _views.push_back (DraggingView (*i, this, tav));
2179 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2182 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2183 // we added all the regions after the selection
2185 std::list<DraggingView>::iterator to_erase = i++;
2186 if (!_editor->selection->regions.contains (to_erase->view)) {
2187 // restore the non-selected regions to their original playlist & positions,
2188 // and then ripple them back by the length of the regions that were dragged away
2189 // do the same things as RegionMotionDrag::aborted
2191 RegionView *rv = to_erase->view;
2192 TimeAxisView* tv = &(rv->get_time_axis_view ());
2193 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2196 // plonk them back onto their own track
2197 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2198 rv->get_canvas_group()->set_y_position (0);
2202 // move the underlying region to match the view
2203 rv->region()->set_position (rv->region()->position() + amount);
2205 // restore the view to match the underlying region's original position
2206 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2209 rv->set_height (rtv->view()->child_height ());
2210 _views.erase (to_erase);
2216 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2218 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2220 return allow_moves_across_tracks;
2228 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2229 : RegionMoveDrag (e, i, p, v, false, false)
2231 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2232 // compute length of selection
2233 RegionSelection selected_regions = _editor->selection->regions;
2234 selection_length = selected_regions.end_frame() - selected_regions.start();
2236 // we'll only allow dragging to another track in ripple mode if all the regions
2237 // being dragged start off on the same track
2238 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2241 exclude = new RegionList;
2242 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2243 exclude->push_back((*i)->region());
2246 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2247 RegionSelection copy;
2248 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2250 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2251 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2253 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2254 // find ripple start point on each applicable playlist
2255 RegionView *first_selected_on_this_track = NULL;
2256 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2257 if ((*i)->region()->playlist() == (*pi)) {
2258 // region is on this playlist - it's the first, because they're sorted
2259 first_selected_on_this_track = *i;
2263 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2264 add_all_after_to_views (
2265 &first_selected_on_this_track->get_time_axis_view(),
2266 first_selected_on_this_track->region()->position(),
2267 selected_regions, false);
2270 if (allow_moves_across_tracks) {
2271 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2279 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2281 /* Which trackview is this ? */
2283 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2284 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2286 /* The region motion is only processed if the pointer is over
2290 if (!tv || !tv->is_track()) {
2291 /* To make sure we hide the verbose canvas cursor when the mouse is
2292 not held over an audiotrack.
2294 _editor->verbose_cursor()->hide ();
2298 framepos_t where = adjusted_current_frame (event);
2299 assert (where >= 0);
2300 MusicFrame after (0, 0);
2301 double delta = compute_x_delta (event, &after);
2303 framecnt_t amount = _editor->pixel_to_sample (delta);
2305 if (allow_moves_across_tracks) {
2306 // all the originally selected regions were on the same track
2308 framecnt_t adjust = 0;
2309 if (prev_tav && tv != prev_tav) {
2310 // dragged onto a different track
2311 // remove the unselected regions from _views, restore them to their original positions
2312 // and add the regions after the drop point on the new playlist to _views instead.
2313 // undo the effect of rippling the previous playlist, and include the effect of removing
2314 // the dragged region(s) from this track
2316 remove_unselected_from_views (prev_amount, false);
2317 // ripple previous playlist according to the regions that have been removed onto the new playlist
2318 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2321 // move just the selected regions
2322 RegionMoveDrag::motion(event, first_move);
2324 // ensure that the ripple operation on the new playlist inserts selection_length time
2325 adjust = selection_length;
2326 // ripple the new current playlist
2327 tv->playlist()->ripple (where, amount+adjust, exclude);
2329 // add regions after point where drag entered this track to subsequent ripples
2330 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2333 // motion on same track
2334 RegionMoveDrag::motion(event, first_move);
2338 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2339 prev_position = where;
2341 // selection encompasses multiple tracks - just drag
2342 // cross-track drags are forbidden
2343 RegionMoveDrag::motion(event, first_move);
2346 if (!_x_constrained) {
2347 prev_amount += amount;
2350 _last_position = after;
2354 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2356 if (!movement_occurred) {
2360 if (was_double_click() && !_views.empty()) {
2361 DraggingView dv = _views.front();
2362 _editor->edit_region (dv.view);
2368 _editor->begin_reversible_command(_("Ripple drag"));
2370 // remove the regions being rippled from the dragging view, updating them to
2371 // their new positions
2372 remove_unselected_from_views (prev_amount, true);
2374 if (allow_moves_across_tracks) {
2376 // if regions were dragged across tracks, we've rippled any later
2377 // regions on the track the regions were dragged off, so we need
2378 // to add the original track to the undo record
2379 orig_tav->playlist()->clear_changes();
2380 vector<Command*> cmds;
2381 orig_tav->playlist()->rdiff (cmds);
2382 _editor->session()->add_commands (cmds);
2384 if (prev_tav && prev_tav != orig_tav) {
2385 prev_tav->playlist()->clear_changes();
2386 vector<Command*> cmds;
2387 prev_tav->playlist()->rdiff (cmds);
2388 _editor->session()->add_commands (cmds);
2391 // selection spanned multiple tracks - all will need adding to undo record
2393 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2394 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2396 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2397 (*pi)->clear_changes();
2398 vector<Command*> cmds;
2399 (*pi)->rdiff (cmds);
2400 _editor->session()->add_commands (cmds);
2404 // other modified playlists are added to undo by RegionMoveDrag::finished()
2405 RegionMoveDrag::finished (event, movement_occurred);
2406 _editor->commit_reversible_command();
2410 RegionRippleDrag::aborted (bool movement_occurred)
2412 RegionMoveDrag::aborted (movement_occurred);
2417 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2419 _view (dynamic_cast<MidiTimeAxisView*> (v))
2421 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2427 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2431 _editor->begin_reversible_command (_("create region"));
2432 _region = add_midi_region (_view, false);
2433 _view->playlist()->freeze ();
2437 framepos_t const f = adjusted_current_frame (event);
2438 if (f <= grab_frame()) {
2439 _region->set_initial_position (f);
2442 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2443 so that if this region is duplicated, its duplicate starts on
2444 a snap point rather than 1 frame after a snap point. Otherwise things get
2445 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2446 place snapped notes at the start of the region.
2448 if (f != grab_frame()) {
2449 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2450 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2457 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2459 if (!movement_occurred) {
2460 add_midi_region (_view, true);
2462 _view->playlist()->thaw ();
2463 _editor->commit_reversible_command();
2468 RegionCreateDrag::aborted (bool)
2471 _view->playlist()->thaw ();
2477 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2482 , _was_selected (false)
2485 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2489 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2491 Gdk::Cursor* cursor;
2492 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2494 float x_fraction = cnote->mouse_x_fraction ();
2496 if (x_fraction > 0.0 && x_fraction < 0.25) {
2497 cursor = _editor->cursors()->left_side_trim;
2500 cursor = _editor->cursors()->right_side_trim;
2504 Drag::start_grab (event, cursor);
2506 region = &cnote->region_view();
2509 temp = region->snap_to_pixel (cnote->x0 (), true);
2510 _snap_delta = temp - cnote->x0 ();
2514 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2519 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2520 if (ms.size() > 1) {
2521 /* has to be relative, may make no sense otherwise */
2525 if (!(_was_selected = cnote->selected())) {
2527 /* tertiary-click means extend selection - we'll do that on button release,
2528 so don't add it here, because otherwise we make it hard to figure
2529 out the "extend-to" range.
2532 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2535 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2538 region->note_selected (cnote, true);
2540 _editor->get_selection().clear_points();
2541 region->unique_select (cnote);
2548 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2550 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2552 _editor->begin_reversible_command (_("resize notes"));
2554 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2555 MidiRegionSelection::iterator next;
2558 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2560 mrv->begin_resizing (at_front);
2566 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2567 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2569 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2573 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2575 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2576 if (_editor->snap_mode () != SnapOff) {
2580 if (_editor->snap_mode () == SnapOff) {
2582 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2583 if (apply_snap_delta) {
2589 if (apply_snap_delta) {
2593 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2599 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2601 if (!movement_occurred) {
2602 /* no motion - select note */
2603 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2604 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2605 _editor->current_mouse_mode() == Editing::MouseDraw) {
2607 bool changed = false;
2609 if (_was_selected) {
2610 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2612 region->note_deselected (cnote);
2615 _editor->get_selection().clear_points();
2616 region->unique_select (cnote);
2620 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2621 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2623 if (!extend && !add && region->selection_size() > 1) {
2624 _editor->get_selection().clear_points();
2625 region->unique_select (cnote);
2627 } else if (extend) {
2628 region->note_selected (cnote, true, true);
2631 /* it was added during button press */
2637 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2638 _editor->commit_reversible_selection_op();
2645 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2646 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2647 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2649 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2652 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2654 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2655 if (_editor->snap_mode () != SnapOff) {
2659 if (_editor->snap_mode () == SnapOff) {
2661 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2662 if (apply_snap_delta) {
2668 if (apply_snap_delta) {
2672 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2676 _editor->commit_reversible_command ();
2680 NoteResizeDrag::aborted (bool)
2682 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2683 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2684 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2686 mrv->abort_resizing ();
2691 AVDraggingView::AVDraggingView (RegionView* v)
2694 initial_position = v->region()->position ();
2697 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2700 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2703 TrackViewList empty;
2705 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2706 std::list<RegionView*> views = rs.by_layer();
2709 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2710 RegionView* rv = (*i);
2711 if (!rv->region()->video_locked()) {
2714 if (rv->region()->locked()) {
2717 _views.push_back (AVDraggingView (rv));
2722 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2724 Drag::start_grab (event);
2725 if (_editor->session() == 0) {
2729 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2735 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2739 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2740 _max_backwards_drag = (
2741 ARDOUR_UI::instance()->video_timeline->get_duration()
2742 + ARDOUR_UI::instance()->video_timeline->get_offset()
2743 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2746 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2747 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2748 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2751 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2754 Timecode::Time timecode;
2755 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2756 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);
2757 show_verbose_cursor_text (buf);
2761 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2763 if (_editor->session() == 0) {
2766 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2770 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2774 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2775 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2777 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2778 dt = - _max_backwards_drag;
2781 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2782 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2784 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2785 RegionView* rv = i->view;
2786 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2789 rv->region()->clear_changes ();
2790 rv->region()->suspend_property_changes();
2792 rv->region()->set_position(i->initial_position + dt);
2793 rv->region_changed(ARDOUR::Properties::position);
2796 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2797 Timecode::Time timecode;
2798 Timecode::Time timediff;
2800 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2801 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2802 snprintf (buf, sizeof (buf),
2803 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2804 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2805 , _("Video Start:"),
2806 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2808 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2810 show_verbose_cursor_text (buf);
2814 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2816 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2823 if (!movement_occurred || ! _editor->session()) {
2827 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2829 _editor->begin_reversible_command (_("Move Video"));
2831 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2832 ARDOUR_UI::instance()->video_timeline->save_undo();
2833 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2834 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2836 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2837 i->view->drag_end();
2838 i->view->region()->resume_property_changes ();
2840 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2843 _editor->session()->maybe_update_session_range(
2844 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2845 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2849 _editor->commit_reversible_command ();
2853 VideoTimeLineDrag::aborted (bool)
2855 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2858 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2859 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2861 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2862 i->view->region()->resume_property_changes ();
2863 i->view->region()->set_position(i->initial_position);
2867 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2868 : RegionDrag (e, i, p, v)
2869 , _operation (StartTrim)
2870 , _preserve_fade_anchor (preserve_fade_anchor)
2871 , _jump_position_when_done (false)
2873 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2877 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2880 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2881 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2883 if (tv && tv->is_track()) {
2884 speed = tv->track()->speed();
2887 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2888 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2889 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2891 framepos_t const pf = adjusted_current_frame (event);
2892 setup_snap_delta (MusicFrame(region_start, 0));
2894 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2895 /* Move the contents of the region around without changing the region bounds */
2896 _operation = ContentsTrim;
2897 Drag::start_grab (event, _editor->cursors()->trimmer);
2899 /* These will get overridden for a point trim.*/
2900 if (pf < (region_start + region_length/2)) {
2901 /* closer to front */
2902 _operation = StartTrim;
2903 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2904 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2906 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2910 _operation = EndTrim;
2911 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2912 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2914 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2918 /* jump trim disabled for now
2919 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2920 _jump_position_when_done = true;
2924 switch (_operation) {
2926 show_verbose_cursor_time (region_start);
2929 show_verbose_cursor_duration (region_start, region_end);
2932 show_verbose_cursor_time (pf);
2936 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2937 i->view->region()->suspend_property_changes ();
2942 TrimDrag::motion (GdkEvent* event, bool first_move)
2944 RegionView* rv = _primary;
2947 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2948 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2949 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2950 frameoffset_t frame_delta = 0;
2952 if (tv && tv->is_track()) {
2953 speed = tv->track()->speed();
2955 MusicFrame adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2956 framecnt_t dt = adj_frame.frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2962 switch (_operation) {
2964 trim_type = "Region start trim";
2967 trim_type = "Region end trim";
2970 trim_type = "Region content trim";
2977 _editor->begin_reversible_command (trim_type);
2979 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2980 RegionView* rv = i->view;
2981 rv->region()->playlist()->clear_owned_changes ();
2983 if (_operation == StartTrim) {
2984 rv->trim_front_starting ();
2987 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2990 arv->temporarily_hide_envelope ();
2994 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2995 insert_result = _editor->motion_frozen_playlists.insert (pl);
2997 if (insert_result.second) {
3001 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (rv);
3002 /* a MRV start trim may change the source length. ensure we cover all playlists here */
3003 if (mrv && _operation == StartTrim) {
3004 vector<boost::shared_ptr<Playlist> > all_playlists;
3005 _editor->session()->playlists->get (all_playlists);
3006 for (vector<boost::shared_ptr<Playlist> >::iterator x = all_playlists.begin(); x != all_playlists.end(); ++x) {
3008 if ((*x)->uses_source (rv->region()->source(0))) {
3009 insert_result = _editor->motion_frozen_playlists.insert (*x);
3010 if (insert_result.second) {
3011 (*x)->clear_owned_changes ();
3021 bool non_overlap_trim = false;
3023 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
3024 non_overlap_trim = true;
3027 /* contstrain trim to fade length */
3028 if (_preserve_fade_anchor) {
3029 switch (_operation) {
3031 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3032 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3034 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3035 if (ar->locked()) continue;
3036 framecnt_t len = ar->fade_in()->back()->when;
3037 if (len < dt) dt = min(dt, len);
3041 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3042 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3044 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3045 if (ar->locked()) continue;
3046 framecnt_t len = ar->fade_out()->back()->when;
3047 if (len < -dt) dt = max(dt, -len);
3055 switch (_operation) {
3057 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3058 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
3059 , adj_frame.division);
3061 if (changed && _preserve_fade_anchor) {
3062 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3064 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3065 framecnt_t len = ar->fade_in()->back()->when;
3066 framecnt_t diff = ar->first_frame() - i->initial_position;
3067 framepos_t new_length = len - diff;
3068 i->anchored_fade_length = min (ar->length(), new_length);
3069 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
3070 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
3077 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3078 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, adj_frame.division);
3079 if (changed && _preserve_fade_anchor) {
3080 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3082 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3083 framecnt_t len = ar->fade_out()->back()->when;
3084 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
3085 framepos_t new_length = len + diff;
3086 i->anchored_fade_length = min (ar->length(), new_length);
3087 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
3088 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3096 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3098 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3099 i->view->move_contents (frame_delta);
3105 switch (_operation) {
3107 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3110 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3113 // show_verbose_cursor_time (frame_delta);
3119 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3121 if (movement_occurred) {
3122 motion (event, false);
3124 if (_operation == StartTrim) {
3125 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3127 /* This must happen before the region's StatefulDiffCommand is created, as it may
3128 `correct' (ahem) the region's _start from being negative to being zero. It
3129 needs to be zero in the undo record.
3131 i->view->trim_front_ending ();
3133 if (_preserve_fade_anchor) {
3134 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3136 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3137 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3138 ar->set_fade_in_length(i->anchored_fade_length);
3139 ar->set_fade_in_active(true);
3142 if (_jump_position_when_done) {
3143 i->view->region()->set_position (i->initial_position);
3146 } else if (_operation == EndTrim) {
3147 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3148 if (_preserve_fade_anchor) {
3149 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3151 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3152 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3153 ar->set_fade_out_length(i->anchored_fade_length);
3154 ar->set_fade_out_active(true);
3157 if (_jump_position_when_done) {
3158 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3163 if (!_editor->selection->selected (_primary)) {
3164 _primary->thaw_after_trim ();
3166 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3167 i->view->thaw_after_trim ();
3168 i->view->enable_display (true);
3172 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3173 /* Trimming one region may affect others on the playlist, so we need
3174 to get undo Commands from the whole playlist rather than just the
3175 region. Use motion_frozen_playlists (a set) to make sure we don't
3176 diff a given playlist more than once.
3179 vector<Command*> cmds;
3181 _editor->session()->add_commands (cmds);
3185 _editor->motion_frozen_playlists.clear ();
3186 _editor->commit_reversible_command();
3189 /* no mouse movement */
3190 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false).frame) {
3191 _editor->point_trim (event, adjusted_current_frame (event));
3195 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3196 i->view->region()->resume_property_changes ();
3201 TrimDrag::aborted (bool movement_occurred)
3203 /* Our motion method is changing model state, so use the Undo system
3204 to cancel. Perhaps not ideal, as this will leave an Undo point
3205 behind which may be slightly odd from the user's point of view.
3209 finished (&ev, true);
3211 if (movement_occurred) {
3212 _editor->session()->undo (1);
3215 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3216 i->view->region()->resume_property_changes ();
3221 TrimDrag::setup_pointer_frame_offset ()
3223 list<DraggingView>::iterator i = _views.begin ();
3224 while (i != _views.end() && i->view != _primary) {
3228 if (i == _views.end()) {
3232 switch (_operation) {
3234 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3237 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3244 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3247 , _old_snap_type (e->snap_type())
3248 , _old_snap_mode (e->snap_mode())
3251 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3252 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3254 _real_section = &_marker->meter();
3259 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3261 Drag::start_grab (event, cursor);
3262 show_verbose_cursor_time (adjusted_current_frame(event));
3266 MeterMarkerDrag::setup_pointer_frame_offset ()
3268 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3272 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3275 // create a dummy marker to catch events, then hide it.
3278 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3280 _marker = new MeterMarker (
3282 *_editor->meter_group,
3283 UIConfiguration::instance().color ("meter marker"),
3285 *new MeterSection (_marker->meter())
3288 /* use the new marker for the grab */
3289 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3292 TempoMap& map (_editor->session()->tempo_map());
3293 /* get current state */
3294 before_state = &map.get_state();
3297 _editor->begin_reversible_command (_("move meter mark"));
3299 _editor->begin_reversible_command (_("copy meter mark"));
3301 Timecode::BBT_Time bbt = _real_section->bbt();
3303 /* we can't add a meter where one currently exists */
3304 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3309 const double beat = map.beat_at_bbt (bbt);
3310 const framepos_t frame = map.frame_at_beat (beat);
3311 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3312 , beat, bbt, frame, _real_section->position_lock_style());
3313 if (!_real_section) {
3319 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3320 if (_real_section->position_lock_style() != AudioTime) {
3321 _editor->set_snap_to (SnapToBar);
3322 _editor->set_snap_mode (SnapNormal);
3326 framepos_t pf = adjusted_current_frame (event);
3328 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3329 /* never snap to music for audio locked */
3330 pf = adjusted_current_frame (event, false);
3333 _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3335 /* fake marker meeds to stay under the mouse, unlike the real one. */
3336 _marker->set_position (adjusted_current_frame (event, false));
3338 show_verbose_cursor_time (_real_section->frame());
3342 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3344 if (!movement_occurred) {
3345 if (was_double_click()) {
3346 _editor->edit_meter_marker (*_marker);
3351 /* reinstate old snap setting */
3352 _editor->set_snap_to (_old_snap_type);
3353 _editor->set_snap_mode (_old_snap_mode);
3355 TempoMap& map (_editor->session()->tempo_map());
3357 XMLNode &after = map.get_state();
3358 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3359 _editor->commit_reversible_command ();
3361 // delete the dummy marker we used for visual representation while moving.
3362 // a new visual marker will show up automatically.
3367 MeterMarkerDrag::aborted (bool moved)
3369 _marker->set_position (_marker->meter().frame ());
3371 /* reinstate old snap setting */
3372 _editor->set_snap_to (_old_snap_type);
3373 _editor->set_snap_mode (_old_snap_mode);
3375 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3376 // delete the dummy marker we used for visual representation while moving.
3377 // a new visual marker will show up automatically.
3382 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3385 , _grab_bpm (120.0, 4.0)
3389 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3391 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3392 _real_section = &_marker->tempo();
3393 _movable = !_real_section->initial();
3394 _grab_bpm = Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), _real_section->end_note_types_per_minute());
3395 _grab_qn = _real_section->pulse() * 4.0;
3400 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3402 Drag::start_grab (event, cursor);
3403 if (!_real_section->active()) {
3404 show_verbose_cursor_text (_("inactive"));
3406 show_verbose_cursor_time (adjusted_current_frame (event));
3411 TempoMarkerDrag::setup_pointer_frame_offset ()
3413 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3417 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3419 if (!_real_section->active()) {
3422 TempoMap& map (_editor->session()->tempo_map());
3426 // mvc drag - create a dummy marker to catch events, hide it.
3429 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3431 TempoSection section (_marker->tempo());
3433 _marker = new TempoMarker (
3435 *_editor->tempo_group,
3436 UIConfiguration::instance().color ("tempo marker"),
3438 *new TempoSection (_marker->tempo())
3441 /* use the new marker for the grab */
3442 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3445 /* get current state */
3446 _before_state = &map.get_state();
3449 _editor->begin_reversible_command (_("move tempo mark"));
3452 const Tempo tempo (_marker->tempo());
3453 const framepos_t frame = adjusted_current_frame (event) + 1;
3455 _editor->begin_reversible_command (_("copy tempo mark"));
3457 if (_real_section->position_lock_style() == MusicTime) {
3458 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3459 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, MusicTime);
3461 _real_section = map.add_tempo (tempo, 0.0, frame, AudioTime);
3464 if (!_real_section) {
3471 if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
3472 double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3474 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), new_bpm));
3475 strs << "end:" << fixed << setprecision(3) << new_bpm;
3476 show_verbose_cursor_text (strs.str());
3478 } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3479 /* use vertical movement to alter tempo .. should be log */
3480 double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3482 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type(), _real_section->end_note_types_per_minute()));
3483 strs << "start:" << fixed << setprecision(3) << new_bpm;
3484 show_verbose_cursor_text (strs.str());
3486 } else if (_movable && !_real_section->locked_to_meter()) {
3489 if (_editor->snap_musical()) {
3490 /* we can't snap to a grid that we are about to move.
3491 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3493 pf = adjusted_current_frame (event, false);
3495 pf = adjusted_current_frame (event);
3498 /* snap to beat is 1, snap to bar is -1 (sorry) */
3499 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3501 map.gui_set_tempo_position (_real_section, pf, sub_num);
3503 show_verbose_cursor_time (_real_section->frame());
3505 _marker->set_position (adjusted_current_frame (event, false));
3509 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3511 if (!_real_section->active()) {
3514 if (!movement_occurred) {
3515 if (was_double_click()) {
3516 _editor->edit_tempo_marker (*_marker);
3521 TempoMap& map (_editor->session()->tempo_map());
3523 XMLNode &after = map.get_state();
3524 _editor->session()->add_command (new MementoCommand<TempoMap>(map, _before_state, &after));
3525 _editor->commit_reversible_command ();
3527 // delete the dummy marker we used for visual representation while moving.
3528 // a new visual marker will show up automatically.
3533 TempoMarkerDrag::aborted (bool moved)
3535 _marker->set_position (_marker->tempo().frame());
3537 TempoMap& map (_editor->session()->tempo_map());
3538 map.set_state (*_before_state, Stateful::current_state_version);
3539 // delete the dummy (hidden) marker we used for events while moving.
3544 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3550 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3555 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3557 Drag::start_grab (event, cursor);
3558 TempoMap& map (_editor->session()->tempo_map());
3559 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3560 _editor->tempo_curve_selected (_tempo, true);
3563 if (_tempo->clamped()) {
3564 TempoSection* prev = map.previous_tempo_section (_tempo);
3566 _editor->tempo_curve_selected (prev, true);
3567 sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3571 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3572 show_verbose_cursor_text (sstr.str());
3576 BBTRulerDrag::setup_pointer_frame_offset ()
3578 TempoMap& map (_editor->session()->tempo_map());
3579 /* get current state */
3580 _before_state = &map.get_state();
3582 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3583 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3586 if (divisions > 0) {
3587 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3589 /* while it makes some sense for the user to determine the division to 'grab',
3590 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3591 and the result over steep tempo curves. Use sixteenths.
3593 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3596 _grab_qn = map.quarter_note_at_beat (beat);
3598 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3603 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3605 TempoMap& map (_editor->session()->tempo_map());
3608 _editor->begin_reversible_command (_("stretch tempo"));
3613 if (_editor->snap_musical()) {
3614 pf = adjusted_current_frame (event, false);
3616 pf = adjusted_current_frame (event);
3619 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3620 /* adjust previous tempo to match pointer frame */
3621 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3625 if (_tempo->clamped()) {
3626 TempoSection* prev = map.previous_tempo_section (_tempo);
3628 _editor->tempo_curve_selected (prev, true);
3629 sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3632 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3633 show_verbose_cursor_text (sstr.str());
3637 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3639 if (!movement_occurred) {
3643 TempoMap& map (_editor->session()->tempo_map());
3645 XMLNode &after = map.get_state();
3646 _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
3647 _editor->commit_reversible_command ();
3648 _editor->tempo_curve_selected (_tempo, false);
3650 if (_tempo->clamped()) {
3651 TempoSection* prev_tempo = map.previous_tempo_section (_tempo);
3653 _editor->tempo_curve_selected (prev_tempo, false);
3659 BBTRulerDrag::aborted (bool moved)
3662 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3666 TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
3672 , _drag_valid (true)
3675 DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
3680 TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3682 Drag::start_grab (event, cursor);
3683 TempoMap& map (_editor->session()->tempo_map());
3684 /* get current state */
3685 _before_state = &map.get_state();
3686 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3688 _next_tempo = map.next_tempo_section (_tempo);
3690 if (!map.next_tempo_section (_next_tempo)) {
3691 _drag_valid = false;
3692 finished (event, false);
3696 _editor->tempo_curve_selected (_tempo, true);
3697 _editor->tempo_curve_selected (_next_tempo, true);
3700 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3701 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3702 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3703 show_verbose_cursor_text (sstr.str());
3705 _drag_valid = false;
3708 _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
3712 TempoTwistDrag::setup_pointer_frame_offset ()
3714 TempoMap& map (_editor->session()->tempo_map());
3715 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3716 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3719 if (divisions > 0) {
3720 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3722 /* while it makes some sense for the user to determine the division to 'grab',
3723 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3724 and the result over steep tempo curves. Use sixteenths.
3726 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3729 _grab_qn = map.quarter_note_at_beat (beat);
3731 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3736 TempoTwistDrag::motion (GdkEvent* event, bool first_move)
3739 if (!_next_tempo || !_drag_valid) {
3743 TempoMap& map (_editor->session()->tempo_map());
3746 _editor->begin_reversible_command (_("twist tempo"));
3751 if (_editor->snap_musical()) {
3752 pf = adjusted_current_frame (event, false);
3754 pf = adjusted_current_frame (event);
3757 /* adjust this and the next tempi to match pointer frame */
3758 double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3759 _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.frame_at_quarter_note (_grab_qn), pf);
3762 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3763 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3764 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3765 show_verbose_cursor_text (sstr.str());
3769 TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
3771 TempoMap& map (_editor->session()->tempo_map());
3773 if (!movement_occurred || !_drag_valid) {
3777 _editor->tempo_curve_selected (_tempo, false);
3778 _editor->tempo_curve_selected (_next_tempo, false);
3780 XMLNode &after = map.get_state();
3781 _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
3782 _editor->commit_reversible_command ();
3786 TempoTwistDrag::aborted (bool moved)
3789 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3793 TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
3799 DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
3800 TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3801 _tempo = &marker->tempo();
3802 _grab_qn = _tempo->pulse() * 4.0;
3806 TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3808 Drag::start_grab (event, cursor);
3809 TempoMap& tmap (_editor->session()->tempo_map());
3811 /* get current state */
3812 _before_state = &tmap.get_state();
3817 TempoSection* prev = 0;
3818 if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3819 _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true);
3820 sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3823 if (_tempo->clamped()) {
3824 _editor->tempo_curve_selected (_tempo, true);
3825 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3828 show_verbose_cursor_text (sstr.str());
3832 TempoEndDrag::setup_pointer_frame_offset ()
3834 TempoMap& map (_editor->session()->tempo_map());
3836 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3841 TempoEndDrag::motion (GdkEvent* event, bool first_move)
3843 TempoMap& map (_editor->session()->tempo_map());
3846 _editor->begin_reversible_command (_("stretch end tempo"));
3851 framepos_t const pf = adjusted_current_frame (event, false);
3852 map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
3855 sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3857 if (_tempo->clamped()) {
3858 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3861 show_verbose_cursor_text (sstr.str());
3865 TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
3867 if (!movement_occurred) {
3871 TempoMap& tmap (_editor->session()->tempo_map());
3873 XMLNode &after = tmap.get_state();
3874 _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after));
3875 _editor->commit_reversible_command ();
3877 TempoSection* prev = 0;
3878 if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3879 _editor->tempo_curve_selected (prev, false);
3882 if (_tempo->clamped()) {
3883 _editor->tempo_curve_selected (_tempo, false);
3889 TempoEndDrag::aborted (bool moved)
3892 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3896 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3897 : Drag (e, &c.track_canvas_item(), false)
3902 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3905 /** Do all the things we do when dragging the playhead to make it look as though
3906 * we have located, without actually doing the locate (because that would cause
3907 * the diskstream buffers to be refilled, which is too slow).
3910 CursorDrag::fake_locate (framepos_t t)
3912 if (_editor->session () == 0) {
3916 _editor->playhead_cursor->set_position (t);
3918 Session* s = _editor->session ();
3919 if (s->timecode_transmission_suspended ()) {
3920 framepos_t const f = _editor->playhead_cursor->current_frame ();
3921 /* This is asynchronous so it will be sent "now"
3923 s->send_mmc_locate (f);
3924 /* These are synchronous and will be sent during the next
3927 s->queue_full_time_code ();
3928 s->queue_song_position_pointer ();
3931 show_verbose_cursor_time (t);
3932 _editor->UpdateAllTransportClocks (t);
3936 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3938 Drag::start_grab (event, c);
3939 setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
3941 _grab_zoom = _editor->samples_per_pixel;
3943 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3945 _editor->snap_to_with_modifier (where, event);
3946 _editor->_dragging_playhead = true;
3947 _editor->_control_scroll_target = where.frame;
3949 Session* s = _editor->session ();
3951 /* grab the track canvas item as well */
3953 _cursor.track_canvas_item().grab();
3956 if (_was_rolling && _stop) {
3960 if (s->is_auditioning()) {
3961 s->cancel_audition ();
3965 if (AudioEngine::instance()->running()) {
3967 /* do this only if we're the engine is connected
3968 * because otherwise this request will never be
3969 * serviced and we'll busy wait forever. likewise,
3970 * notice if we are disconnected while waiting for the
3971 * request to be serviced.
3974 s->request_suspend_timecode_transmission ();
3975 while (AudioEngine::instance()->running() && !s->timecode_transmission_suspended ()) {
3976 /* twiddle our thumbs */
3981 fake_locate (where.frame - snap_delta (event->button.state));
3985 CursorDrag::motion (GdkEvent* event, bool)
3987 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3989 _editor->snap_to_with_modifier (where, event);
3991 if (where.frame != last_pointer_frame()) {
3992 fake_locate (where.frame - snap_delta (event->button.state));
3997 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3999 _editor->_dragging_playhead = false;
4001 _cursor.track_canvas_item().ungrab();
4003 if (!movement_occurred && _stop) {
4007 motion (event, false);
4009 Session* s = _editor->session ();
4011 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
4012 _editor->_pending_locate_request = true;
4013 s->request_resume_timecode_transmission ();
4018 CursorDrag::aborted (bool)
4020 _cursor.track_canvas_item().ungrab();
4022 if (_editor->_dragging_playhead) {
4023 _editor->session()->request_resume_timecode_transmission ();
4024 _editor->_dragging_playhead = false;
4027 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false).frame);
4030 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4031 : RegionDrag (e, i, p, v)
4033 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
4037 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4039 Drag::start_grab (event, cursor);
4041 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4042 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4043 setup_snap_delta (MusicFrame (r->position(), 0));
4045 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
4049 FadeInDrag::setup_pointer_frame_offset ()
4051 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4052 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4053 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
4057 FadeInDrag::motion (GdkEvent* event, bool)
4059 framecnt_t fade_length;
4061 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4062 _editor->snap_to_with_modifier (pos, event);
4064 pos.frame -= snap_delta (event->button.state);
4066 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4068 if (pos.frame < (region->position() + 64)) {
4069 fade_length = 64; // this should be a minimum defined somewhere
4070 } else if (pos.frame > region->position() + region->length() - region->fade_out()->back()->when) {
4071 fade_length = region->length() - region->fade_out()->back()->when - 1;
4073 fade_length = pos.frame - region->position();
4076 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4078 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4084 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
4087 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
4091 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
4093 if (!movement_occurred) {
4097 framecnt_t fade_length;
4098 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4100 _editor->snap_to_with_modifier (pos, event);
4101 pos.frame -= snap_delta (event->button.state);
4103 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4105 if (pos.frame < (region->position() + 64)) {
4106 fade_length = 64; // this should be a minimum defined somewhere
4107 } else if (pos.frame >= region->position() + region->length() - region->fade_out()->back()->when) {
4108 fade_length = region->length() - region->fade_out()->back()->when - 1;
4110 fade_length = pos.frame - region->position();
4113 bool in_command = false;
4115 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4117 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4123 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4124 XMLNode &before = alist->get_state();
4126 tmp->audio_region()->set_fade_in_length (fade_length);
4127 tmp->audio_region()->set_fade_in_active (true);
4130 _editor->begin_reversible_command (_("change fade in length"));
4133 XMLNode &after = alist->get_state();
4134 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4138 _editor->commit_reversible_command ();
4143 FadeInDrag::aborted (bool)
4145 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4146 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4152 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
4156 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4157 : RegionDrag (e, i, p, v)
4159 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
4163 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4165 Drag::start_grab (event, cursor);
4167 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4168 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4169 setup_snap_delta (MusicFrame (r->last_frame(), 0));
4171 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
4175 FadeOutDrag::setup_pointer_frame_offset ()
4177 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4178 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4179 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
4183 FadeOutDrag::motion (GdkEvent* event, bool)
4185 framecnt_t fade_length;
4186 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4188 _editor->snap_to_with_modifier (pos, event);
4189 pos.frame -= snap_delta (event->button.state);
4191 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4193 if (pos.frame > (region->last_frame() - 64)) {
4194 fade_length = 64; // this should really be a minimum fade defined somewhere
4195 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4196 fade_length = region->length() - region->fade_in()->back()->when - 1;
4198 fade_length = region->last_frame() - pos.frame;
4201 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4203 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4209 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
4212 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
4216 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
4218 if (!movement_occurred) {
4222 framecnt_t fade_length;
4223 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4225 _editor->snap_to_with_modifier (pos, event);
4226 pos.frame -= snap_delta (event->button.state);
4228 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4230 if (pos.frame > (region->last_frame() - 64)) {
4231 fade_length = 64; // this should really be a minimum fade defined somewhere
4232 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4233 fade_length = region->length() - region->fade_in()->back()->when - 1;
4235 fade_length = region->last_frame() - pos.frame;
4238 bool in_command = false;
4240 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4242 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4248 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
4249 XMLNode &before = alist->get_state();
4251 tmp->audio_region()->set_fade_out_length (fade_length);
4252 tmp->audio_region()->set_fade_out_active (true);
4255 _editor->begin_reversible_command (_("change fade out length"));
4258 XMLNode &after = alist->get_state();
4259 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4263 _editor->commit_reversible_command ();
4268 FadeOutDrag::aborted (bool)
4270 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4271 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4277 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
4281 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4283 , _selection_changed (false)
4285 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4286 Gtk::Window* toplevel = _editor->current_toplevel();
4287 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4291 _points.push_back (ArdourCanvas::Duple (0, 0));
4293 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4296 MarkerDrag::~MarkerDrag ()
4298 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4303 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4305 location = new Location (*l);
4306 markers.push_back (m);
4311 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4313 Drag::start_grab (event, cursor);
4317 Location *location = _editor->find_location_from_marker (_marker, is_start);
4318 _editor->_dragging_edit_point = true;
4320 update_item (location);
4322 // _drag_line->show();
4323 // _line->raise_to_top();
4326 show_verbose_cursor_time (location->start());
4328 show_verbose_cursor_time (location->end());
4330 setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
4332 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4335 case Selection::Toggle:
4336 /* we toggle on the button release */
4338 case Selection::Set:
4339 if (!_editor->selection->selected (_marker)) {
4340 _editor->selection->set (_marker);
4341 _selection_changed = true;
4344 case Selection::Extend:
4346 Locations::LocationList ll;
4347 list<ArdourMarker*> to_add;
4349 _editor->selection->markers.range (s, e);
4350 s = min (_marker->position(), s);
4351 e = max (_marker->position(), e);
4354 if (e < max_framepos) {
4357 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4358 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4359 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4362 to_add.push_back (lm->start);
4365 to_add.push_back (lm->end);
4369 if (!to_add.empty()) {
4370 _editor->selection->add (to_add);
4371 _selection_changed = true;
4375 case Selection::Add:
4376 _editor->selection->add (_marker);
4377 _selection_changed = true;
4382 /* Set up copies for us to manipulate during the drag
4385 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4387 Location* l = _editor->find_location_from_marker (*i, is_start);
4394 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4396 /* range: check that the other end of the range isn't
4399 CopiedLocationInfo::iterator x;
4400 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4401 if (*(*x).location == *l) {
4405 if (x == _copied_locations.end()) {
4406 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4408 (*x).markers.push_back (*i);
4409 (*x).move_both = true;
4417 MarkerDrag::setup_pointer_frame_offset ()
4420 Location *location = _editor->find_location_from_marker (_marker, is_start);
4421 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4425 MarkerDrag::motion (GdkEvent* event, bool)
4427 framecnt_t f_delta = 0;
4429 bool move_both = false;
4430 Location *real_location;
4431 Location *copy_location = 0;
4432 framecnt_t const sd = snap_delta (event->button.state);
4434 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true).frame - sd;
4435 framepos_t next = newframe;
4437 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4441 CopiedLocationInfo::iterator x;
4443 /* find the marker we're dragging, and compute the delta */
4445 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4447 copy_location = (*x).location;
4449 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4451 /* this marker is represented by this
4452 * CopiedLocationMarkerInfo
4455 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4460 if (real_location->is_mark()) {
4461 f_delta = newframe - copy_location->start();
4465 switch (_marker->type()) {
4466 case ArdourMarker::SessionStart:
4467 case ArdourMarker::RangeStart:
4468 case ArdourMarker::LoopStart:
4469 case ArdourMarker::PunchIn:
4470 f_delta = newframe - copy_location->start();
4473 case ArdourMarker::SessionEnd:
4474 case ArdourMarker::RangeEnd:
4475 case ArdourMarker::LoopEnd:
4476 case ArdourMarker::PunchOut:
4477 f_delta = newframe - copy_location->end();
4480 /* what kind of marker is this ? */
4489 if (x == _copied_locations.end()) {
4490 /* hmm, impossible - we didn't find the dragged marker */
4494 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4496 /* now move them all */
4498 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4500 copy_location = x->location;
4502 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4506 if (real_location->locked()) {
4510 if (copy_location->is_mark()) {
4513 copy_location->set_start (copy_location->start() + f_delta, false, true, divisions);
4517 framepos_t new_start = copy_location->start() + f_delta;
4518 framepos_t new_end = copy_location->end() + f_delta;
4520 if (is_start) { // start-of-range marker
4522 if (move_both || (*x).move_both) {
4523 copy_location->set_start (new_start, false, true, divisions);
4524 copy_location->set_end (new_end, false, true, divisions);
4525 } else if (new_start < copy_location->end()) {
4526 copy_location->set_start (new_start, false, true, divisions);
4527 } else if (newframe > 0) {
4528 //_editor->snap_to (next, RoundUpAlways, true);
4529 copy_location->set_end (next, false, true, divisions);
4530 copy_location->set_start (newframe, false, true, divisions);
4533 } else { // end marker
4535 if (move_both || (*x).move_both) {
4536 copy_location->set_end (new_end, divisions);
4537 copy_location->set_start (new_start, false, true, divisions);
4538 } else if (new_end > copy_location->start()) {
4539 copy_location->set_end (new_end, false, true, divisions);
4540 } else if (newframe > 0) {
4541 //_editor->snap_to (next, RoundDownAlways, true);
4542 copy_location->set_start (next, false, true, divisions);
4543 copy_location->set_end (newframe, false, true, divisions);
4548 update_item (copy_location);
4550 /* now lookup the actual GUI items used to display this
4551 * location and move them to wherever the copy of the location
4552 * is now. This means that the logic in ARDOUR::Location is
4553 * still enforced, even though we are not (yet) modifying
4554 * the real Location itself.
4557 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4560 lm->set_position (copy_location->start(), copy_location->end());
4565 assert (!_copied_locations.empty());
4567 show_verbose_cursor_time (newframe);
4571 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4573 if (!movement_occurred) {
4575 if (was_double_click()) {
4576 _editor->rename_marker (_marker);
4580 /* just a click, do nothing but finish
4581 off the selection process
4584 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4586 case Selection::Set:
4587 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4588 _editor->selection->set (_marker);
4589 _selection_changed = true;
4593 case Selection::Toggle:
4594 /* we toggle on the button release, click only */
4595 _editor->selection->toggle (_marker);
4596 _selection_changed = true;
4600 case Selection::Extend:
4601 case Selection::Add:
4605 if (_selection_changed) {
4606 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4607 _editor->commit_reversible_selection_op();
4613 _editor->_dragging_edit_point = false;
4615 XMLNode &before = _editor->session()->locations()->get_state();
4616 bool in_command = false;
4618 MarkerSelection::iterator i;
4619 CopiedLocationInfo::iterator x;
4620 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4623 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4624 x != _copied_locations.end() && i != _editor->selection->markers.end();
4627 Location * location = _editor->find_location_from_marker (*i, is_start);
4631 if (location->locked()) {
4635 _editor->begin_reversible_command ( _("move marker") );
4638 if (location->is_mark()) {
4639 location->set_start (((*x).location)->start(), false, true, divisions);
4641 location->set (((*x).location)->start(), ((*x).location)->end(), true, divisions);
4644 if (location->is_session_range()) {
4645 _editor->session()->set_end_is_free (false);
4651 XMLNode &after = _editor->session()->locations()->get_state();
4652 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4653 _editor->commit_reversible_command ();
4658 MarkerDrag::aborted (bool movement_occurred)
4660 if (!movement_occurred) {
4664 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4666 /* move all markers to their original location */
4669 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4672 Location * location = _editor->find_location_from_marker (*m, is_start);
4675 (*m)->set_position (is_start ? location->start() : location->end());
4682 MarkerDrag::update_item (Location*)
4687 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4689 , _fixed_grab_x (0.0)
4690 , _fixed_grab_y (0.0)
4691 , _cumulative_x_drag (0.0)
4692 , _cumulative_y_drag (0.0)
4696 if (_zero_gain_fraction < 0.0) {
4697 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4700 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4702 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4708 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4710 Drag::start_grab (event, _editor->cursors()->fader);
4712 // start the grab at the center of the control point so
4713 // the point doesn't 'jump' to the mouse after the first drag
4714 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4715 _fixed_grab_y = _point->get_y();
4717 setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
4719 float const fraction = 1 - (_point->get_y() / _point->line().height());
4720 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4722 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4724 if (!_point->can_slide ()) {
4725 _x_constrained = true;
4730 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4732 double dx = _drags->current_pointer_x() - last_pointer_x();
4733 double dy = current_pointer_y() - last_pointer_y();
4734 bool need_snap = true;
4736 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4742 /* coordinate in pixels relative to the start of the region (for region-based automation)
4743 or track (for track-based automation) */
4744 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4745 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4747 // calculate zero crossing point. back off by .01 to stay on the
4748 // positive side of zero
4749 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4751 if (_x_constrained) {
4754 if (_y_constrained) {
4758 _cumulative_x_drag = cx - _fixed_grab_x;
4759 _cumulative_y_drag = cy - _fixed_grab_y;
4763 cy = min ((double) _point->line().height(), cy);
4765 // make sure we hit zero when passing through
4766 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4770 MusicFrame cx_mf (_editor->pixel_to_sample (cx) + snap_delta (event->button.state), 0);
4772 if (!_x_constrained && need_snap) {
4773 _editor->snap_to_with_modifier (cx_mf, event);
4776 cx_mf.frame -= snap_delta (event->button.state);
4777 cx_mf.frame = min (cx_mf.frame, _point->line().maximum_time() + _point->line().offset());
4779 float const fraction = 1.0 - (cy / _point->line().height());
4782 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4783 _editor->begin_reversible_command (_("automation event move"));
4784 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4786 pair<double, float> result;
4787 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
4789 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4793 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4795 if (!movement_occurred) {
4798 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4799 _editor->reset_point_selection ();
4803 _point->line().end_drag (_pushing, _final_index);
4804 _editor->commit_reversible_command ();
4809 ControlPointDrag::aborted (bool)
4811 _point->line().reset ();
4815 ControlPointDrag::active (Editing::MouseMode m)
4817 if (m == Editing::MouseDraw) {
4818 /* always active in mouse draw */
4822 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4823 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4826 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4829 , _fixed_grab_x (0.0)
4830 , _fixed_grab_y (0.0)
4831 , _cumulative_y_drag (0)
4835 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4839 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4841 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4844 _item = &_line->grab_item ();
4846 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4847 origin, and ditto for y.
4850 double mx = event->button.x;
4851 double my = event->button.y;
4853 _line->grab_item().canvas_to_item (mx, my);
4855 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4857 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4858 /* no adjacent points */
4862 Drag::start_grab (event, _editor->cursors()->fader);
4864 /* store grab start in item frame */
4865 double const bx = _line->nth (_before)->get_x();
4866 double const ax = _line->nth (_after)->get_x();
4867 double const click_ratio = (ax - mx) / (ax - bx);
4869 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4874 double fraction = 1.0 - (cy / _line->height());
4876 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4880 LineDrag::motion (GdkEvent* event, bool first_move)
4882 double dy = current_pointer_y() - last_pointer_y();
4884 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4888 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4890 _cumulative_y_drag = cy - _fixed_grab_y;
4893 cy = min ((double) _line->height(), cy);
4895 double const fraction = 1.0 - (cy / _line->height());
4899 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4901 _editor->begin_reversible_command (_("automation range move"));
4902 _line->start_drag_line (_before, _after, initial_fraction);
4905 /* we are ignoring x position for this drag, so we can just pass in anything */
4906 pair<double, float> result;
4908 result = _line->drag_motion (0, fraction, true, false, ignored);
4909 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4913 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4915 if (movement_occurred) {
4916 motion (event, false);
4917 _line->end_drag (false, 0);
4918 _editor->commit_reversible_command ();
4920 /* add a new control point on the line */
4922 AutomationTimeAxisView* atv;
4924 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4925 framepos_t where = grab_frame ();
4928 double cy = _fixed_grab_y;
4930 _line->grab_item().item_to_canvas (cx, cy);
4932 atv->add_automation_event (event, where, cy, false);
4933 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4934 AudioRegionView* arv;
4936 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4937 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4944 LineDrag::aborted (bool)
4949 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4953 _region_view_grab_x (0.0),
4954 _cumulative_x_drag (0),
4958 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4962 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4964 Drag::start_grab (event);
4966 _line = reinterpret_cast<Line*> (_item);
4969 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4971 double cx = event->button.x;
4972 double cy = event->button.y;
4974 _item->parent()->canvas_to_item (cx, cy);
4976 /* store grab start in parent frame */
4977 _region_view_grab_x = cx;
4979 _before = *(float*) _item->get_data ("position");
4981 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4983 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4987 FeatureLineDrag::motion (GdkEvent*, bool)
4989 double dx = _drags->current_pointer_x() - last_pointer_x();
4991 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4993 _cumulative_x_drag += dx;
4995 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
5004 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
5006 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
5008 float *pos = new float;
5011 _line->set_data ("position", pos);
5017 FeatureLineDrag::finished (GdkEvent*, bool)
5019 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
5020 _arv->update_transient(_before, _before);
5024 FeatureLineDrag::aborted (bool)
5029 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5031 , _vertical_only (false)
5033 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
5037 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5039 Drag::start_grab (event);
5040 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
5044 RubberbandSelectDrag::motion (GdkEvent* event, bool)
5050 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
5051 MusicFrame grab (grab_frame (), 0);
5053 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5054 _editor->snap_to_with_modifier (grab, event);
5056 grab.frame = raw_grab_frame ();
5059 /* base start and end on initial click position */
5061 if (pf < grab.frame) {
5069 if (current_pointer_y() < grab_y()) {
5070 y1 = current_pointer_y();
5073 y2 = current_pointer_y();
5077 if (start != end || y1 != y2) {
5079 double x1 = _editor->sample_to_pixel (start);
5080 double x2 = _editor->sample_to_pixel (end);
5081 const double min_dimension = 2.0;
5083 if (_vertical_only) {
5084 /* fixed 10 pixel width */
5088 x2 = min (x1 - min_dimension, x2);
5090 x2 = max (x1 + min_dimension, x2);
5095 y2 = min (y1 - min_dimension, y2);
5097 y2 = max (y1 + min_dimension, y2);
5100 /* translate rect into item space and set */
5102 ArdourCanvas::Rect r (x1, y1, x2, y2);
5104 /* this drag is a _trackview_only == true drag, so the y1 and
5105 * y2 (computed using current_pointer_y() and grab_y()) will be
5106 * relative to the top of the trackview group). The
5107 * rubberband rect has the same parent/scroll offset as the
5108 * the trackview group, so we can use the "r" rect directly
5109 * to set the shape of the rubberband.
5112 _editor->rubberband_rect->set (r);
5113 _editor->rubberband_rect->show();
5114 _editor->rubberband_rect->raise_to_top();
5116 show_verbose_cursor_time (pf);
5118 do_select_things (event, true);
5123 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
5127 framepos_t grab = grab_frame ();
5128 framepos_t lpf = last_pointer_frame ();
5130 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5131 grab = raw_grab_frame ();
5132 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
5146 if (current_pointer_y() < grab_y()) {
5147 y1 = current_pointer_y();
5150 y2 = current_pointer_y();
5154 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
5158 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
5160 if (movement_occurred) {
5162 motion (event, false);
5163 do_select_things (event, false);
5169 bool do_deselect = true;
5170 MidiTimeAxisView* mtv;
5172 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
5174 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
5175 /* nothing selected */
5176 add_midi_region (mtv, true);
5177 do_deselect = false;
5181 /* do not deselect if Primary or Tertiary (toggle-select or
5182 * extend-select are pressed.
5185 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
5186 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
5193 _editor->rubberband_rect->hide();
5197 RubberbandSelectDrag::aborted (bool)
5199 _editor->rubberband_rect->hide ();
5202 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
5203 : RegionDrag (e, i, p, v)
5205 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
5209 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5211 Drag::start_grab (event, cursor);
5213 _editor->get_selection().add (_primary);
5215 MusicFrame where (_primary->region()->position(), 0);
5216 setup_snap_delta (where);
5218 show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
5222 TimeFXDrag::motion (GdkEvent* event, bool)
5224 RegionView* rv = _primary;
5225 StreamView* cv = rv->get_time_axis_view().view ();
5226 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
5227 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
5228 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
5229 MusicFrame pf (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
5231 _editor->snap_to_with_modifier (pf, event);
5232 pf.frame -= snap_delta (event->button.state);
5234 if (pf.frame > rv->region()->position()) {
5235 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf.frame, layers, layer);
5238 show_verbose_cursor_duration (_primary->region()->position(), pf.frame, 0);
5242 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
5244 /* this may have been a single click, no drag. We still want the dialog
5245 to show up in that case, so that the user can manually edit the
5246 parameters for the timestretch.
5249 float fraction = 1.0;
5251 if (movement_occurred) {
5253 motion (event, false);
5255 _primary->get_time_axis_view().hide_timestretch ();
5257 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
5259 if (adjusted_frame_pos < _primary->region()->position()) {
5260 /* backwards drag of the left edge - not usable */
5264 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
5266 fraction = (double) newlen / (double) _primary->region()->length();
5268 #ifndef USE_RUBBERBAND
5269 // Soundtouch uses fraction / 100 instead of normal (/ 1)
5270 if (_primary->region()->data_type() == DataType::AUDIO) {
5271 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
5276 if (!_editor->get_selection().regions.empty()) {
5277 /* primary will already be included in the selection, and edit
5278 group shared editing will propagate selection across
5279 equivalent regions, so just use the current region
5283 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5284 error << _("An error occurred while executing time stretch operation") << endmsg;
5290 TimeFXDrag::aborted (bool)
5292 _primary->get_time_axis_view().hide_timestretch ();
5295 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5298 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5302 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5304 Drag::start_grab (event);
5308 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5310 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5314 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5316 if (movement_occurred && _editor->session()) {
5317 /* make sure we stop */
5318 _editor->session()->request_transport_speed (0.0);
5323 ScrubDrag::aborted (bool)
5328 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5332 , _track_selection_at_start (e)
5333 , _time_selection_at_start (!_editor->get_selection().time.empty())
5335 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5337 if (_time_selection_at_start) {
5338 start_at_start = _editor->get_selection().time.start();
5339 end_at_start = _editor->get_selection().time.end_frame();
5344 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5346 if (_editor->session() == 0) {
5350 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5352 switch (_operation) {
5353 case CreateSelection:
5354 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5359 cursor = _editor->cursors()->selector;
5360 Drag::start_grab (event, cursor);
5363 case SelectionStartTrim:
5364 if (_editor->clicked_axisview) {
5365 _editor->clicked_axisview->order_selection_trims (_item, true);
5367 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5370 case SelectionEndTrim:
5371 if (_editor->clicked_axisview) {
5372 _editor->clicked_axisview->order_selection_trims (_item, false);
5374 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5378 Drag::start_grab (event, cursor);
5381 case SelectionExtend:
5382 Drag::start_grab (event, cursor);
5386 if (_operation == SelectionMove) {
5387 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5389 show_verbose_cursor_time (adjusted_current_frame (event));
5394 SelectionDrag::setup_pointer_frame_offset ()
5396 switch (_operation) {
5397 case CreateSelection:
5398 _pointer_frame_offset = 0;
5401 case SelectionStartTrim:
5403 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5406 case SelectionEndTrim:
5407 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5410 case SelectionExtend:
5416 SelectionDrag::motion (GdkEvent* event, bool first_move)
5418 framepos_t start = 0;
5420 framecnt_t length = 0;
5421 framecnt_t distance = 0;
5422 MusicFrame start_mf (0, 0);
5423 framepos_t const pending_position = adjusted_current_frame (event);
5425 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5430 _track_selection_at_start = _editor->selection->tracks;
5433 switch (_operation) {
5434 case CreateSelection:
5436 MusicFrame grab (grab_frame (), 0);
5438 grab.frame = adjusted_current_frame (event, false);
5439 if (grab.frame < pending_position) {
5440 _editor->snap_to (grab, RoundDownMaybe);
5442 _editor->snap_to (grab, RoundUpMaybe);
5446 if (pending_position < grab.frame) {
5447 start = pending_position;
5450 end = pending_position;
5454 /* first drag: Either add to the selection
5455 or create a new selection
5462 /* adding to the selection */
5463 _editor->set_selected_track_as_side_effect (Selection::Add);
5464 _editor->clicked_selection = _editor->selection->add (start, end);
5471 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5472 _editor->set_selected_track_as_side_effect (Selection::Set);
5475 _editor->clicked_selection = _editor->selection->set (start, end);
5479 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5480 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5481 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5483 _editor->selection->add (atest);
5487 /* select all tracks within the rectangle that we've marked out so far */
5488 TrackViewList new_selection;
5489 TrackViewList& all_tracks (_editor->track_views);
5491 ArdourCanvas::Coord const top = grab_y();
5492 ArdourCanvas::Coord const bottom = current_pointer_y();
5494 if (top >= 0 && bottom >= 0) {
5496 //first, find the tracks that are covered in the y range selection
5497 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5498 if ((*i)->covered_by_y_range (top, bottom)) {
5499 new_selection.push_back (*i);
5503 //now compare our list with the current selection, and add as necessary
5504 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5505 TrackViewList tracks_to_add;
5506 TrackViewList tracks_to_remove;
5507 vector<RouteGroup*> selected_route_groups;
5510 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
5511 if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
5512 tracks_to_remove.push_back (*i);
5514 RouteGroup* rg = (*i)->route_group();
5515 if (rg && rg->is_active() && rg->is_select()) {
5516 selected_route_groups.push_back (rg);
5522 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5523 if (!_editor->selection->tracks.contains (*i)) {
5524 tracks_to_add.push_back (*i);
5525 RouteGroup* rg = (*i)->route_group();
5527 if (rg && rg->is_active() && rg->is_select()) {
5528 selected_route_groups.push_back (rg);
5533 _editor->selection->add (tracks_to_add);
5535 if (!tracks_to_remove.empty()) {
5537 /* check all these to-be-removed tracks against the
5538 * possibility that they are selected by being
5539 * in the same group as an approved track.
5542 for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
5543 RouteGroup* rg = (*i)->route_group();
5545 if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
5546 i = tracks_to_remove.erase (i);
5552 /* remove whatever is left */
5554 _editor->selection->remove (tracks_to_remove);
5560 case SelectionStartTrim:
5562 end = _editor->selection->time[_editor->clicked_selection].end;
5564 if (pending_position > end) {
5567 start = pending_position;
5571 case SelectionEndTrim:
5573 start = _editor->selection->time[_editor->clicked_selection].start;
5575 if (pending_position < start) {
5578 end = pending_position;
5585 start = _editor->selection->time[_editor->clicked_selection].start;
5586 end = _editor->selection->time[_editor->clicked_selection].end;
5588 length = end - start;
5589 distance = pending_position - start;
5590 start = pending_position;
5592 start_mf.frame = start;
5593 _editor->snap_to (start_mf);
5595 end = start_mf.frame + length;
5599 case SelectionExtend:
5604 switch (_operation) {
5606 if (_time_selection_at_start) {
5607 _editor->selection->move_time (distance);
5611 _editor->selection->replace (_editor->clicked_selection, start, end);
5615 if (_operation == SelectionMove) {
5616 show_verbose_cursor_time(start);
5618 show_verbose_cursor_time(pending_position);
5623 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5625 Session* s = _editor->session();
5627 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5628 if (movement_occurred) {
5629 motion (event, false);
5630 /* XXX this is not object-oriented programming at all. ick */
5631 if (_editor->selection->time.consolidate()) {
5632 _editor->selection->TimeChanged ();
5635 /* XXX what if its a music time selection? */
5637 if (s->get_play_range() && s->transport_rolling()) {
5638 s->request_play_range (&_editor->selection->time, true);
5639 } else if (!s->config.get_external_sync()) {
5640 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5641 s->request_locate (_editor->get_selection().time.start());
5645 if (_editor->get_selection().time.length() != 0) {
5646 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5648 s->clear_range_selection ();
5653 /* just a click, no pointer movement.
5656 if (was_double_click()) {
5657 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5658 _editor->temporal_zoom_selection (Both);
5663 if (_operation == SelectionExtend) {
5664 if (_time_selection_at_start) {
5665 framepos_t pos = adjusted_current_frame (event, false);
5666 framepos_t start = min (pos, start_at_start);
5667 framepos_t end = max (pos, end_at_start);
5668 _editor->selection->set (start, end);
5671 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5672 if (_editor->clicked_selection) {
5673 _editor->selection->remove (_editor->clicked_selection);
5676 if (!_editor->clicked_selection) {
5677 _editor->selection->clear_time();
5682 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5683 _editor->selection->set (_editor->clicked_axisview);
5686 if (s && s->get_play_range () && s->transport_rolling()) {
5687 s->request_stop (false, false);
5692 _editor->stop_canvas_autoscroll ();
5693 _editor->clicked_selection = 0;
5694 _editor->commit_reversible_selection_op ();
5698 SelectionDrag::aborted (bool)
5703 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5704 : Drag (e, i, false),
5708 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5710 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5711 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5712 physical_screen_height (_editor->current_toplevel()->get_window())));
5713 _drag_rect->hide ();
5715 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5716 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5719 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5721 /* normal canvas items will be cleaned up when their parent group is deleted. But
5722 this item is created as the child of a long-lived parent group, and so we
5723 need to explicitly delete it.
5729 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5731 if (_editor->session() == 0) {
5735 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5737 if (!_editor->temp_location) {
5738 _editor->temp_location = new Location (*_editor->session());
5741 switch (_operation) {
5742 case CreateSkipMarker:
5743 case CreateRangeMarker:
5744 case CreateTransportMarker:
5745 case CreateCDMarker:
5747 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5752 cursor = _editor->cursors()->selector;
5756 Drag::start_grab (event, cursor);
5758 show_verbose_cursor_time (adjusted_current_frame (event));
5762 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5764 framepos_t start = 0;
5766 ArdourCanvas::Rectangle *crect;
5768 switch (_operation) {
5769 case CreateSkipMarker:
5770 crect = _editor->range_bar_drag_rect;
5772 case CreateRangeMarker:
5773 crect = _editor->range_bar_drag_rect;
5775 case CreateTransportMarker:
5776 crect = _editor->transport_bar_drag_rect;
5778 case CreateCDMarker:
5779 crect = _editor->cd_marker_bar_drag_rect;
5782 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5787 framepos_t const pf = adjusted_current_frame (event);
5789 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5790 MusicFrame grab (grab_frame (), 0);
5791 _editor->snap_to (grab);
5793 if (pf < grab_frame()) {
5801 /* first drag: Either add to the selection
5802 or create a new selection.
5807 _editor->temp_location->set (start, end);
5811 update_item (_editor->temp_location);
5813 //_drag_rect->raise_to_top();
5819 _editor->temp_location->set (start, end);
5821 double x1 = _editor->sample_to_pixel (start);
5822 double x2 = _editor->sample_to_pixel (end);
5826 update_item (_editor->temp_location);
5829 show_verbose_cursor_time (pf);
5834 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5836 Location * newloc = 0;
5840 if (movement_occurred) {
5841 motion (event, false);
5844 switch (_operation) {
5845 case CreateSkipMarker:
5846 case CreateRangeMarker:
5847 case CreateCDMarker:
5849 XMLNode &before = _editor->session()->locations()->get_state();
5850 if (_operation == CreateSkipMarker) {
5851 _editor->begin_reversible_command (_("new skip marker"));
5852 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5853 flags = Location::IsRangeMarker | Location::IsSkip;
5854 _editor->range_bar_drag_rect->hide();
5855 } else if (_operation == CreateCDMarker) {
5856 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5857 _editor->begin_reversible_command (_("new CD marker"));
5858 flags = Location::IsRangeMarker | Location::IsCDMarker;
5859 _editor->cd_marker_bar_drag_rect->hide();
5861 _editor->begin_reversible_command (_("new skip marker"));
5862 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5863 flags = Location::IsRangeMarker;
5864 _editor->range_bar_drag_rect->hide();
5866 newloc = new Location (
5867 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5868 , _editor->get_grid_music_divisions (event->button.state));
5870 _editor->session()->locations()->add (newloc, true);
5871 XMLNode &after = _editor->session()->locations()->get_state();
5872 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5873 _editor->commit_reversible_command ();
5877 case CreateTransportMarker:
5878 // popup menu to pick loop or punch
5879 _editor->new_transport_marker_context_menu (&event->button, _item);
5885 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5887 if (_operation == CreateTransportMarker) {
5889 /* didn't drag, so just locate */
5891 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5893 } else if (_operation == CreateCDMarker) {
5895 /* didn't drag, but mark is already created so do
5898 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5903 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5905 if (end == max_framepos) {
5906 end = _editor->session()->current_end_frame ();
5909 if (start == max_framepos) {
5910 start = _editor->session()->current_start_frame ();
5913 switch (_editor->mouse_mode) {
5915 /* find the two markers on either side and then make the selection from it */
5916 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5920 /* find the two markers on either side of the click and make the range out of it */
5921 _editor->selection->set (start, end);
5930 _editor->stop_canvas_autoscroll ();
5934 RangeMarkerBarDrag::aborted (bool movement_occurred)
5936 if (movement_occurred) {
5937 _drag_rect->hide ();
5942 RangeMarkerBarDrag::update_item (Location* location)
5944 double const x1 = _editor->sample_to_pixel (location->start());
5945 double const x2 = _editor->sample_to_pixel (location->end());
5947 _drag_rect->set_x0 (x1);
5948 _drag_rect->set_x1 (x2);
5951 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5953 , _cumulative_dx (0)
5954 , _cumulative_dy (0)
5956 , _was_selected (false)
5959 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5961 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5963 _region = &_primary->region_view ();
5964 _note_height = _region->midi_stream_view()->note_height ();
5968 NoteDrag::setup_pointer_frame_offset ()
5970 _pointer_frame_offset = raw_grab_frame()
5971 - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
5975 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5977 Drag::start_grab (event);
5979 if (ArdourKeyboard::indicates_copy (event->button.state)) {
5985 setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
5987 if (!(_was_selected = _primary->selected())) {
5989 /* tertiary-click means extend selection - we'll do that on button release,
5990 so don't add it here, because otherwise we make it hard to figure
5991 out the "extend-to" range.
5994 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5997 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
6000 _region->note_selected (_primary, true);
6002 _editor->get_selection().clear_points();
6003 _region->unique_select (_primary);
6009 /** @return Current total drag x change in quarter notes */
6011 NoteDrag::total_dx (GdkEvent * event) const
6013 if (_x_constrained) {
6017 TempoMap& map (_editor->session()->tempo_map());
6020 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
6022 /* primary note time */
6023 frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
6025 /* primary note time in quarter notes */
6026 double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
6028 /* new time of the primary note in session frames */
6029 frameoffset_t st = n + dx + snap_delta (event->button.state);
6031 /* possibly snap and return corresponding delta in quarter notes */
6032 MusicFrame snap (st, 0);
6033 _editor->snap_to_with_modifier (snap, event);
6034 double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
6036 /* prevent the earliest note being dragged earlier than the region's start position */
6037 if (_earliest + ret < _region->midi_region()->start_beats()) {
6038 ret -= (_earliest + ret) - _region->midi_region()->start_beats();
6044 /** @return Current total drag y change in note number */
6046 NoteDrag::total_dy () const
6048 if (_y_constrained) {
6052 double const y = _region->midi_view()->y_position ();
6053 /* new current note */
6054 uint8_t n = _region->y_to_note (current_pointer_y () - y);
6056 MidiStreamView* msv = _region->midi_stream_view ();
6057 n = max (msv->lowest_note(), n);
6058 n = min (msv->highest_note(), n);
6059 /* and work out delta */
6060 return n - _region->y_to_note (grab_y() - y);
6064 NoteDrag::motion (GdkEvent * event, bool first_move)
6067 _earliest = _region->earliest_in_selection().to_double();
6069 /* make copies of all the selected notes */
6070 _primary = _region->copy_selection (_primary);
6074 /* Total change in x and y since the start of the drag */
6075 double const dx_qn = total_dx (event);
6076 int8_t const dy = total_dy ();
6078 /* Now work out what we have to do to the note canvas items to set this new drag delta */
6079 double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
6080 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
6083 _cumulative_dx = dx_qn;
6084 _cumulative_dy += tdy;
6086 int8_t note_delta = total_dy();
6090 _region->move_copies (dx_qn, tdy, note_delta);
6092 _region->move_selection (dx_qn, tdy, note_delta);
6095 /* the new note value may be the same as the old one, but we
6096 * don't know what that means because the selection may have
6097 * involved more than one note and we might be doing something
6098 * odd with them. so show the note value anyway, always.
6101 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
6103 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
6109 NoteDrag::finished (GdkEvent* ev, bool moved)
6112 /* no motion - select note */
6114 if (_editor->current_mouse_mode() == Editing::MouseContent ||
6115 _editor->current_mouse_mode() == Editing::MouseDraw) {
6117 bool changed = false;
6119 if (_was_selected) {
6120 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6122 _region->note_deselected (_primary);
6125 _editor->get_selection().clear_points();
6126 _region->unique_select (_primary);
6130 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
6131 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6133 if (!extend && !add && _region->selection_size() > 1) {
6134 _editor->get_selection().clear_points();
6135 _region->unique_select (_primary);
6137 } else if (extend) {
6138 _region->note_selected (_primary, true, true);
6141 /* it was added during button press */
6148 _editor->begin_reversible_selection_op(X_("Select Note Release"));
6149 _editor->commit_reversible_selection_op();
6153 _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
6158 NoteDrag::aborted (bool)
6163 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
6164 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
6165 : Drag (editor, atv->base_item ())
6167 , _y_origin (atv->y_position())
6168 , _nothing_to_drag (false)
6170 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6171 setup (atv->lines ());
6174 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
6175 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
6176 : Drag (editor, rv->get_canvas_group ())
6178 , _y_origin (rv->get_time_axis_view().y_position())
6179 , _nothing_to_drag (false)
6182 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6184 list<boost::shared_ptr<AutomationLine> > lines;
6186 AudioRegionView* audio_view;
6187 AutomationRegionView* automation_view;
6188 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
6189 lines.push_back (audio_view->get_gain_line ());
6190 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
6191 lines.push_back (automation_view->line ());
6194 error << _("Automation range drag created for invalid region type") << endmsg;
6200 /** @param lines AutomationLines to drag.
6201 * @param offset Offset from the session start to the points in the AutomationLines.
6204 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
6206 /* find the lines that overlap the ranges being dragged */
6207 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
6208 while (i != lines.end ()) {
6209 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
6212 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
6214 /* check this range against all the AudioRanges that we are using */
6215 list<AudioRange>::const_iterator k = _ranges.begin ();
6216 while (k != _ranges.end()) {
6217 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
6223 /* add it to our list if it overlaps at all */
6224 if (k != _ranges.end()) {
6229 _lines.push_back (n);
6235 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
6239 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
6241 return 1.0 - ((global_y - _y_origin) / line->height());
6245 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
6247 const double v = list->eval(x);
6248 return _integral ? rint(v) : v;
6252 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6254 Drag::start_grab (event, cursor);
6256 /* Get line states before we start changing things */
6257 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6258 i->state = &i->line->get_state ();
6259 i->original_fraction = y_fraction (i->line, current_pointer_y());
6262 if (_ranges.empty()) {
6264 /* No selected time ranges: drag all points */
6265 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6266 uint32_t const N = i->line->npoints ();
6267 for (uint32_t j = 0; j < N; ++j) {
6268 i->points.push_back (i->line->nth (j));
6274 if (_nothing_to_drag) {
6280 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
6282 if (_nothing_to_drag && !first_move) {
6287 _editor->begin_reversible_command (_("automation range move"));
6289 if (!_ranges.empty()) {
6291 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
6293 framecnt_t const half = (i->start + i->end) / 2;
6295 /* find the line that this audio range starts in */
6296 list<Line>::iterator j = _lines.begin();
6297 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
6301 if (j != _lines.end()) {
6302 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6304 /* j is the line that this audio range starts in; fade into it;
6305 64 samples length plucked out of thin air.
6308 framepos_t a = i->start + 64;
6313 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
6314 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
6316 XMLNode &before = the_list->get_state();
6317 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6318 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6320 if (add_p || add_q) {
6321 _editor->session()->add_command (
6322 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6326 /* same thing for the end */
6329 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6333 if (j != _lines.end()) {
6334 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6336 /* j is the line that this audio range starts in; fade out of it;
6337 64 samples length plucked out of thin air.
6340 framepos_t b = i->end - 64;
6345 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6346 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6348 XMLNode &before = the_list->get_state();
6349 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6350 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6352 if (add_p || add_q) {
6353 _editor->session()->add_command (
6354 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6359 _nothing_to_drag = true;
6361 /* Find all the points that should be dragged and put them in the relevant
6362 points lists in the Line structs.
6365 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6367 uint32_t const N = i->line->npoints ();
6368 for (uint32_t j = 0; j < N; ++j) {
6370 /* here's a control point on this line */
6371 ControlPoint* p = i->line->nth (j);
6372 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6374 /* see if it's inside a range */
6375 list<AudioRange>::const_iterator k = _ranges.begin ();
6376 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6380 if (k != _ranges.end()) {
6381 /* dragging this point */
6382 _nothing_to_drag = false;
6383 i->points.push_back (p);
6389 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6390 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6394 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6395 float const f = y_fraction (l->line, current_pointer_y());
6396 /* we are ignoring x position for this drag, so we can just pass in anything */
6397 pair<double, float> result;
6399 result = l->line->drag_motion (0, f, true, false, ignored);
6400 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6405 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6407 if (_nothing_to_drag || !motion_occurred) {
6411 motion (event, false);
6412 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6413 i->line->end_drag (false, 0);
6416 _editor->commit_reversible_command ();
6420 AutomationRangeDrag::aborted (bool)
6422 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6427 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6429 , initial_time_axis_view (itav)
6431 /* note that time_axis_view may be null if the regionview was created
6432 * as part of a copy operation.
6434 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6435 layer = v->region()->layer ();
6436 initial_y = v->get_canvas_group()->position().y;
6437 initial_playlist = v->region()->playlist ();
6438 initial_position = v->region()->position ();
6439 initial_end = v->region()->position () + v->region()->length ();
6442 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6443 : Drag (e, i->canvas_item ())
6446 , _cumulative_dx (0)
6448 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6449 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6454 PatchChangeDrag::motion (GdkEvent* ev, bool)
6456 framepos_t f = adjusted_current_frame (ev);
6457 boost::shared_ptr<Region> r = _region_view->region ();
6458 f = max (f, r->position ());
6459 f = min (f, r->last_frame ());
6461 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6462 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6463 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6464 _cumulative_dx = dxu;
6468 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6470 if (!movement_occurred) {
6471 if (was_double_click()) {
6472 _region_view->edit_patch_change (_patch_change);
6477 boost::shared_ptr<Region> r (_region_view->region ());
6478 framepos_t f = adjusted_current_frame (ev);
6479 f = max (f, r->position ());
6480 f = min (f, r->last_frame ());
6482 _region_view->move_patch_change (
6484 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6489 PatchChangeDrag::aborted (bool)
6491 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6495 PatchChangeDrag::setup_pointer_frame_offset ()
6497 boost::shared_ptr<Region> region = _region_view->region ();
6498 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6501 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6502 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6509 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6511 _region_view->update_drag_selection (
6513 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6517 MidiRubberbandSelectDrag::deselect_things ()
6522 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6523 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6526 _vertical_only = true;
6530 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6532 double const y = _region_view->midi_view()->y_position ();
6534 y1 = max (0.0, y1 - y);
6535 y2 = max (0.0, y2 - y);
6537 _region_view->update_vertical_drag_selection (
6540 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6545 MidiVerticalSelectDrag::deselect_things ()
6550 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6551 : RubberbandSelectDrag (e, i)
6557 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6559 if (drag_in_progress) {
6560 /* We just want to select things at the end of the drag, not during it */
6564 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6566 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6568 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6570 _editor->commit_reversible_selection_op ();
6574 EditorRubberbandSelectDrag::deselect_things ()
6576 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6578 _editor->selection->clear_tracks();
6579 _editor->selection->clear_regions();
6580 _editor->selection->clear_points ();
6581 _editor->selection->clear_lines ();
6582 _editor->selection->clear_midi_notes ();
6584 _editor->commit_reversible_selection_op();
6587 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6592 _note[0] = _note[1] = 0;
6595 NoteCreateDrag::~NoteCreateDrag ()
6601 NoteCreateDrag::grid_frames (framepos_t t) const
6604 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6605 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6607 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6608 - _region_view->region_beats_to_region_frames (t_beats);
6612 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6614 Drag::start_grab (event, cursor);
6616 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6617 TempoMap& map (_editor->session()->tempo_map());
6619 const framepos_t pf = _drags->current_pointer_frame ();
6620 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6622 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6624 double eqaf = map.exact_qn_at_frame (pf, divisions);
6626 if (divisions != 0) {
6628 const double qaf = map.quarter_note_at_frame (pf);
6630 /* Hack so that we always snap to the note that we are over, instead of snapping
6631 to the next one if we're more than halfway through the one we're over.
6634 const double rem = eqaf - qaf;
6636 eqaf -= grid_beats.to_double();
6640 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6641 /* minimum initial length is grid beats */
6642 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6644 double const x0 = _editor->sample_to_pixel (_note[0]);
6645 double const x1 = _editor->sample_to_pixel (_note[1]);
6646 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6648 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6649 _drag_rect->set_outline_all ();
6650 _drag_rect->set_outline_color (0xffffff99);
6651 _drag_rect->set_fill_color (0xffffff66);
6655 NoteCreateDrag::motion (GdkEvent* event, bool)
6657 TempoMap& map (_editor->session()->tempo_map());
6658 const framepos_t pf = _drags->current_pointer_frame ();
6659 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6660 double eqaf = map.exact_qn_at_frame (pf, divisions);
6662 if (divisions != 0) {
6664 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6666 const double qaf = map.quarter_note_at_frame (pf);
6667 /* Hack so that we always snap to the note that we are over, instead of snapping
6668 to the next one if we're more than halfway through the one we're over.
6671 const double rem = eqaf - qaf;
6673 eqaf -= grid_beats.to_double();
6676 eqaf += grid_beats.to_double();
6678 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6680 double const x0 = _editor->sample_to_pixel (_note[0]);
6681 double const x1 = _editor->sample_to_pixel (_note[1]);
6682 _drag_rect->set_x0 (std::min(x0, x1));
6683 _drag_rect->set_x1 (std::max(x0, x1));
6687 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6689 /* we create a note even if there was no movement */
6690 framepos_t const start = min (_note[0], _note[1]);
6691 framepos_t const start_sess_rel = start + _region_view->region()->position();
6692 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6693 framecnt_t const g = grid_frames (start);
6695 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6699 TempoMap& map (_editor->session()->tempo_map());
6700 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6701 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6703 _editor->begin_reversible_command (_("Create Note"));
6704 _region_view->clear_editor_note_selection();
6705 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6706 _editor->commit_reversible_command ();
6710 NoteCreateDrag::y_to_region (double y) const
6713 _region_view->get_canvas_group()->canvas_to_item (x, y);
6718 NoteCreateDrag::aborted (bool)
6723 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6731 HitCreateDrag::~HitCreateDrag ()
6736 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6738 Drag::start_grab (event, cursor);
6740 TempoMap& map (_editor->session()->tempo_map());
6742 _y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6744 const framepos_t pf = _drags->current_pointer_frame ();
6745 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6747 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6749 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6751 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6755 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6756 Evoral::Beats length = _region_view->get_grid_beats (pf);
6758 _editor->begin_reversible_command (_("Create Hit"));
6759 _region_view->clear_editor_note_selection();
6760 _region_view->create_note_at (start, _y, length, event->button.state, false);
6766 HitCreateDrag::motion (GdkEvent* event, bool)
6768 TempoMap& map (_editor->session()->tempo_map());
6770 const framepos_t pf = _drags->current_pointer_frame ();
6771 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6773 if (divisions == 0) {
6777 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6778 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6780 if (_last_pos == start) {
6784 Evoral::Beats length = _region_view->get_grid_beats (pf);
6786 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6788 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6792 _region_view->create_note_at (start, _y, length, event->button.state, false);
6798 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6800 _editor->commit_reversible_command ();
6805 HitCreateDrag::y_to_region (double y) const
6808 _region_view->get_canvas_group()->canvas_to_item (x, y);
6813 HitCreateDrag::aborted (bool)
6818 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6823 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6827 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6829 Drag::start_grab (event, cursor);
6833 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6839 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6842 distance = _drags->current_pointer_x() - grab_x();
6843 len = ar->fade_in()->back()->when;
6845 distance = grab_x() - _drags->current_pointer_x();
6846 len = ar->fade_out()->back()->when;
6849 /* how long should it be ? */
6851 new_length = len + _editor->pixel_to_sample (distance);
6853 /* now check with the region that this is legal */
6855 new_length = ar->verify_xfade_bounds (new_length, start);
6858 arv->reset_fade_in_shape_width (ar, new_length);
6860 arv->reset_fade_out_shape_width (ar, new_length);
6865 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6871 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6874 distance = _drags->current_pointer_x() - grab_x();
6875 len = ar->fade_in()->back()->when;
6877 distance = grab_x() - _drags->current_pointer_x();
6878 len = ar->fade_out()->back()->when;
6881 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6883 _editor->begin_reversible_command ("xfade trim");
6884 ar->playlist()->clear_owned_changes ();
6887 ar->set_fade_in_length (new_length);
6889 ar->set_fade_out_length (new_length);
6892 /* Adjusting the xfade may affect other regions in the playlist, so we need
6893 to get undo Commands from the whole playlist rather than just the
6897 vector<Command*> cmds;
6898 ar->playlist()->rdiff (cmds);
6899 _editor->session()->add_commands (cmds);
6900 _editor->commit_reversible_command ();
6905 CrossfadeEdgeDrag::aborted (bool)
6908 // arv->redraw_start_xfade ();
6910 // arv->redraw_end_xfade ();
6914 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6915 : Drag (e, item, true)
6916 , line (new EditorCursor (*e))
6918 line->set_position (pos);
6920 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6923 RegionCutDrag::~RegionCutDrag ()
6929 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6931 Drag::start_grab (event, c);
6932 motion (event, false);
6936 RegionCutDrag::motion (GdkEvent* event, bool)
6938 MusicFrame pos (_drags->current_pointer_frame(), 0);
6939 _editor->snap_to_with_modifier (pos, event);
6941 line->set_position (pos.frame);
6945 RegionCutDrag::finished (GdkEvent* event, bool)
6947 _editor->get_track_canvas()->canvas()->re_enter();
6950 MusicFrame pos (_drags->current_pointer_frame(), 0);
6951 _editor->snap_to_with_modifier (pos, event);
6954 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos.frame);
6960 _editor->split_regions_at (pos, rs, false);
6964 RegionCutDrag::aborted (bool)
6968 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6969 : Drag (e, item, true)
6974 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6976 Drag::start_grab (event, c);
6978 framepos_t where = _editor->canvas_event_sample(event);
6980 _editor->_dragging_playhead = true;
6982 _editor->playhead_cursor->set_position (where);
6986 RulerZoomDrag::motion (GdkEvent* event, bool)
6988 framepos_t where = _editor->canvas_event_sample(event);
6990 _editor->playhead_cursor->set_position (where);
6992 const double movement_limit = 20.0;
6993 const double scale = 1.08;
6994 const double y_delta = last_pointer_y() - current_pointer_y();
6996 if (y_delta > 0 && y_delta < movement_limit) {
6997 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6998 } else if (y_delta < 0 && y_delta > -movement_limit) {
6999 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
7004 RulerZoomDrag::finished (GdkEvent*, bool)
7006 _editor->_dragging_playhead = false;
7008 Session* s = _editor->session ();
7010 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
7011 _editor->_pending_locate_request = true;
7017 RulerZoomDrag::aborted (bool)