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/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "ui_config.h"
68 #include "verbose_cursor.h"
71 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
82 DragManager::DragManager (Editor* e)
85 , _current_pointer_x (0.0)
86 , _current_pointer_y (0.0)
87 , _current_pointer_frame (0)
88 , _old_follow_playhead (false)
92 DragManager::~DragManager ()
97 /** Call abort for each active drag */
103 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 if (!_drags.empty ()) {
109 _editor->set_follow_playhead (_old_follow_playhead, false);
113 _editor->abort_reversible_command();
119 DragManager::add (Drag* d)
121 d->set_manager (this);
122 _drags.push_back (d);
126 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
128 d->set_manager (this);
129 _drags.push_back (d);
134 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
136 /* Prevent follow playhead during the drag to be nice to the user */
137 _old_follow_playhead = _editor->follow_playhead ();
138 _editor->set_follow_playhead (false);
140 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
142 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
143 (*i)->start_grab (e, c);
147 /** Call end_grab for each active drag.
148 * @return true if any drag reported movement having occurred.
151 DragManager::end_grab (GdkEvent* e)
156 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
157 bool const t = (*i)->end_grab (e);
168 _editor->set_follow_playhead (_old_follow_playhead, false);
174 DragManager::mark_double_click ()
176 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
177 (*i)->set_double_click (true);
182 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
186 /* calling this implies that we expect the event to have canvas
189 * Can we guarantee that this is true?
192 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
194 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
195 bool const t = (*i)->motion_handler (e, from_autoscroll);
196 /* run all handlers; return true if at least one of them
197 returns true (indicating that the event has been handled).
209 DragManager::have_item (ArdourCanvas::Item* i) const
211 list<Drag*>::const_iterator j = _drags.begin ();
212 while (j != _drags.end() && (*j)->item () != i) {
216 return j != _drags.end ();
219 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
223 , _pointer_frame_offset (0)
224 , _x_constrained (false)
225 , _y_constrained (false)
226 , _was_rolling (false)
227 , _trackview_only (trackview_only)
228 , _move_threshold_passed (false)
229 , _starting_point_passed (false)
230 , _initially_vertical (false)
231 , _was_double_click (false)
234 , _last_pointer_x (0.0)
235 , _last_pointer_y (0.0)
236 , _raw_grab_frame (0)
238 , _last_pointer_frame (0)
245 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
251 _cursor_ctx = CursorContext::create (*_editor, cursor);
253 _cursor_ctx->change (cursor);
260 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
263 /* we set up x/y dragging constraints on first move */
265 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::snap_delta (guint state) const
349 if (ArdourKeyboard::indicates_snap_delta (state)) {
357 Drag::current_pointer_x() const
359 return _drags->current_pointer_x ();
363 Drag::current_pointer_y () const
365 if (!_trackview_only) {
366 return _drags->current_pointer_y ();
369 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
373 Drag::setup_snap_delta (framepos_t pos)
375 framepos_t temp = pos;
376 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
377 _snap_delta = temp - pos;
381 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
383 /* check to see if we have moved in any way that matters since the last motion event */
384 if (_move_threshold_passed &&
385 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
386 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
390 pair<framecnt_t, int> const threshold = move_threshold ();
392 bool const old_move_threshold_passed = _move_threshold_passed;
394 if (!_move_threshold_passed) {
396 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
397 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
399 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
402 if (active (_editor->mouse_mode) && _move_threshold_passed) {
404 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
406 if (old_move_threshold_passed != _move_threshold_passed) {
410 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
411 _initially_vertical = true;
413 _initially_vertical = false;
415 /** check constraints for this drag.
416 * Note that the current convention is to use "contains" for
417 * key modifiers during motion and "equals" when initiating a drag.
418 * In this case we haven't moved yet, so "equals" applies here.
420 if (Config->get_edit_mode() != Lock) {
421 if (event->motion.state & Gdk::BUTTON2_MASK) {
422 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
423 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
424 _x_constrained = false;
425 _y_constrained = true;
427 _x_constrained = true;
428 _y_constrained = false;
430 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
431 // if dragging normally, the motion is constrained to the first direction of movement.
432 if (_initially_vertical) {
433 _x_constrained = true;
434 _y_constrained = false;
436 _x_constrained = false;
437 _y_constrained = true;
441 if (event->button.state & Gdk::BUTTON2_MASK) {
442 _x_constrained = false;
444 _x_constrained = true;
446 _y_constrained = false;
450 if (!from_autoscroll) {
451 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
454 if (!_editor->autoscroll_active() || from_autoscroll) {
457 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
459 motion (event, first_move && !_starting_point_passed);
461 if (first_move && !_starting_point_passed) {
462 _starting_point_passed = true;
465 _last_pointer_x = _drags->current_pointer_x ();
466 _last_pointer_y = current_pointer_y ();
467 _last_pointer_frame = adjusted_current_frame (event, false);
477 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
485 aborted (_move_threshold_passed);
487 _editor->stop_canvas_autoscroll ();
488 _editor->verbose_cursor()->hide ();
492 Drag::show_verbose_cursor_time (framepos_t frame)
494 _editor->verbose_cursor()->set_time (frame);
495 _editor->verbose_cursor()->show ();
499 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
501 _editor->verbose_cursor()->set_duration (start, end);
502 _editor->verbose_cursor()->show ();
506 Drag::show_verbose_cursor_text (string const & text)
508 _editor->verbose_cursor()->set (text);
509 _editor->verbose_cursor()->show ();
512 boost::shared_ptr<Region>
513 Drag::add_midi_region (MidiTimeAxisView* view, bool commit, const int32_t& sub_num)
515 if (_editor->session()) {
516 const TempoMap& map (_editor->session()->tempo_map());
517 framecnt_t pos = grab_frame();
518 /* not that the frame rate used here can be affected by pull up/down which
521 framecnt_t len = map.frame_at_beat (map.beat_at_frame (pos) + 1.0) - pos;
522 return view->add_region (grab_frame(), len, commit, sub_num);
525 return boost::shared_ptr<Region>();
528 struct PresentationInfoTimeAxisViewSorter {
529 bool operator() (TimeAxisView* a, TimeAxisView* b) {
530 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
534 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
539 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
541 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
542 as some of the regions we are dragging may be on such tracks.
545 TrackViewList track_views = _editor->track_views;
546 track_views.sort (PresentationInfoTimeAxisViewSorter ());
548 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
549 _time_axis_views.push_back (*i);
551 TimeAxisView::Children children_list = (*i)->get_child_list ();
552 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
553 _time_axis_views.push_back (j->get());
557 /* the list of views can be empty at this point if this is a region list-insert drag
560 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
561 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
564 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
568 RegionDrag::region_going_away (RegionView* v)
570 list<DraggingView>::iterator i = _views.begin ();
571 while (i != _views.end() && i->view != v) {
575 if (i != _views.end()) {
580 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
581 * or -1 if it is not found.
584 RegionDrag::find_time_axis_view (TimeAxisView* t) const
587 int const N = _time_axis_views.size ();
588 while (i < N && _time_axis_views[i] != t) {
592 if (_time_axis_views[i] != t) {
599 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
600 : RegionDrag (e, i, p, v)
602 , _ignore_video_lock (false)
604 , _last_pointer_time_axis_view (0)
605 , _last_pointer_layer (0)
610 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
614 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
616 Drag::start_grab (event, cursor);
617 setup_snap_delta (_last_frame_position);
619 show_verbose_cursor_time (_last_frame_position);
621 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
623 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
624 assert(_last_pointer_time_axis_view >= 0);
625 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
628 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
629 _ignore_video_lock = true;
633 /* cross track dragging seems broken here. disabled for now. */
634 _y_constrained = true;
639 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
641 /* compute the amount of pointer motion in frames, and where
642 the region would be if we moved it by that much.
644 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
646 framepos_t sync_frame;
647 framecnt_t sync_offset;
650 sync_offset = _primary->region()->sync_offset (sync_dir);
652 /* we don't handle a sync point that lies before zero.
654 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
656 framecnt_t const sd = snap_delta (event->button.state);
657 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
659 _editor->snap_to_with_modifier (sync_frame, event);
661 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
664 *pending_region_position = _last_frame_position;
667 if (*pending_region_position > max_framepos - _primary->region()->length()) {
668 *pending_region_position = _last_frame_position;
673 bool const x_move_allowed = !_x_constrained;
675 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
677 /* x movement since last time (in pixels) */
678 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
680 /* total x movement */
681 framecnt_t total_dx = *pending_region_position;
682 if (regions_came_from_canvas()) {
683 total_dx = total_dx - grab_frame ();
686 /* check that no regions have gone off the start of the session */
687 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
688 if ((i->view->region()->position() + total_dx) < 0) {
690 *pending_region_position = _last_frame_position;
701 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
707 const int tavsize = _time_axis_views.size();
708 const int dt = delta > 0 ? +1 : -1;
710 int target = start + delta - skip;
712 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
713 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
715 while (current >= 0 && current != target) {
717 if (current < 0 && dt < 0) {
720 if (current >= tavsize && dt > 0) {
723 if (current < 0 || current >= tavsize) {
727 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
728 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
732 if (distance_only && current == start + delta) {
740 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
742 if (_y_constrained) {
746 const int tavsize = _time_axis_views.size();
747 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
748 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
749 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
751 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
752 /* already in the drop zone */
753 if (delta_track >= 0) {
754 /* downward motion - OK if others are still not in the dropzone */
763 } else if (n >= tavsize) {
764 /* downward motion into drop zone. That's fine. */
768 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
769 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
770 /* not a track, or the wrong type */
774 double const l = i->layer + delta_layer;
776 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
777 mode to allow the user to place a region below another on layer 0.
779 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
780 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
781 If it has, the layers will be munged later anyway, so it's ok.
787 /* all regions being dragged are ok with this change */
791 struct DraggingViewSorter {
792 bool operator() (const DraggingView& a, const DraggingView& b) {
793 return a.time_axis_view < b.time_axis_view;
798 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
800 double delta_layer = 0;
801 int delta_time_axis_view = 0;
802 int current_pointer_time_axis_view = -1;
804 assert (!_views.empty ());
806 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
808 /* Find the TimeAxisView that the pointer is now over */
809 const double cur_y = current_pointer_y ();
810 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
811 TimeAxisView* tv = r.first;
813 if (!tv && cur_y < 0) {
814 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
818 /* find drop-zone y-position */
819 Coord last_track_bottom_edge;
820 last_track_bottom_edge = 0;
821 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
822 if (!(*t)->hidden()) {
823 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
828 if (tv && tv->view()) {
829 /* the mouse is over a track */
830 double layer = r.second;
832 if (first_move && tv->view()->layer_display() == Stacked) {
833 tv->view()->set_layer_display (Expanded);
836 /* Here's the current pointer position in terms of time axis view and layer */
837 current_pointer_time_axis_view = find_time_axis_view (tv);
838 assert(current_pointer_time_axis_view >= 0);
840 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
842 /* Work out the change in y */
844 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
845 if (!rtv || !rtv->is_track()) {
846 /* ignore non-tracks early on. we can't move any regions on them */
847 } else if (_last_pointer_time_axis_view < 0) {
848 /* Was in the drop-zone, now over a track.
849 * Hence it must be an upward move (from the bottom)
851 * track_index is still -1, so delta must be set to
852 * move up the correct number of tracks from the bottom.
854 * This is necessary because steps may be skipped if
855 * the bottom-most track is not a valid target and/or
856 * if there are hidden tracks at the bottom.
857 * Hence the initial offset (_ddropzone) as well as the
858 * last valid pointer position (_pdropzone) need to be
859 * taken into account.
861 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
863 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
866 /* TODO needs adjustment per DraggingView,
868 * e.g. select one region on the top-layer of a track
869 * and one region which is at the bottom-layer of another track
872 * Indicated drop-zones and layering is wrong.
873 * and may infer additional layers on the target-track
874 * (depending how many layers the original track had).
876 * Or select two regions (different layers) on a same track,
877 * move across a non-layer track.. -> layering info is lost.
878 * on drop either of the regions may be on top.
880 * Proposed solution: screw it :) well,
881 * don't use delta_layer, use an absolute value
882 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
883 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
884 * 3) iterate over all DraggingView, find the one that is over the track with most layers
885 * 4) proportionally scale layer to layers available on target
887 delta_layer = current_pointer_layer - _last_pointer_layer;
890 /* for automation lanes, there is a TimeAxisView but no ->view()
891 * if (!tv) -> dropzone
893 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
894 /* Moving into the drop-zone.. */
895 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
896 /* delta_time_axis_view may not be sufficient to move into the DZ
897 * the mouse may enter it, but it may not be a valid move due to
900 * -> remember the delta needed to move into the dropzone
902 _ddropzone = delta_time_axis_view;
903 /* ..but subtract hidden tracks (or routes) at the bottom.
904 * we silently move mover them
906 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
907 - _time_axis_views.size();
909 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
910 /* move around inside the zone.
911 * This allows to move further down until all regions are in the zone.
913 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
914 assert(ptr_y >= last_track_bottom_edge);
915 assert(_ddropzone > 0);
917 /* calculate mouse position in 'tracks' below last track. */
918 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
919 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
921 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
923 delta_time_axis_view = dzpos - _pdropzone;
924 } else if (dzpos < _pdropzone && _ndropzone > 0) {
925 // move up inside the DZ
926 delta_time_axis_view = dzpos - _pdropzone;
930 /* Work out the change in x */
931 framepos_t pending_region_position;
932 double const x_delta = compute_x_delta (event, &pending_region_position);
933 _last_frame_position = pending_region_position;
935 /* calculate hidden tracks in current y-axis delta */
937 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
938 /* The mouse is more than one track below the dropzone.
939 * distance calculation is not needed (and would not work, either
940 * because the dropzone is "packed").
942 * Except when [partially] moving regions out of dropzone in a large step.
943 * (the mouse may or may not remain in the DZ)
944 * Hidden tracks at the bottom of the TAV need to be skipped.
946 * This also handles the case if the mouse entered the DZ
947 * in a large step (exessive delta), either due to fast-movement,
948 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
950 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
951 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
953 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
954 -_time_axis_views.size() - dt;
957 else if (_last_pointer_time_axis_view < 0) {
958 /* Moving out of the zone. Check for hidden tracks at the bottom. */
959 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
960 -_time_axis_views.size() - delta_time_axis_view;
962 /* calculate hidden tracks that are skipped by the pointer movement */
963 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
964 - _last_pointer_time_axis_view
965 - delta_time_axis_view;
968 /* Verify change in y */
969 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
970 /* this y movement is not allowed, so do no y movement this time */
971 delta_time_axis_view = 0;
976 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
977 /* haven't reached next snap point, and we're not switching
978 trackviews nor layers. nothing to do.
983 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
984 PlaylistDropzoneMap playlist_dropzone_map;
985 _ndropzone = 0; // number of elements currently in the dropzone
988 /* sort views by time_axis.
989 * This retains track order in the dropzone, regardless
990 * of actual selection order
992 _views.sort (DraggingViewSorter());
994 /* count number of distinct tracks of all regions
995 * being dragged, used for dropzone.
998 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
999 if (i->time_axis_view != prev_track) {
1000 prev_track = i->time_axis_view;
1006 _views.back().time_axis_view -
1007 _views.front().time_axis_view;
1009 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1010 - _views.back().time_axis_view;
1012 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1016 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1018 RegionView* rv = i->view;
1023 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1030 /* reparent the regionview into a group above all
1034 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1035 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1036 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1037 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1038 /* move the item so that it continues to appear at the
1039 same location now that its parent has changed.
1041 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1044 /* If we have moved tracks, we'll fudge the layer delta so that the
1045 region gets moved back onto layer 0 on its new track; this avoids
1046 confusion when dragging regions from non-zero layers onto different
1049 double this_delta_layer = delta_layer;
1050 if (delta_time_axis_view != 0) {
1051 this_delta_layer = - i->layer;
1054 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1056 int track_index = i->time_axis_view + this_delta_time_axis_view;
1057 assert(track_index >= 0);
1059 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1060 /* Track is in the Dropzone */
1062 i->time_axis_view = track_index;
1063 assert(i->time_axis_view >= (int) _time_axis_views.size());
1066 double yposition = 0;
1067 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1068 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1071 /* store index of each new playlist as a negative count, starting at -1 */
1073 if (pdz == playlist_dropzone_map.end()) {
1074 /* compute where this new track (which doesn't exist yet) will live
1077 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1079 /* How high is this region view ? */
1081 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1082 ArdourCanvas::Rect bbox;
1085 bbox = obbox.get ();
1088 last_track_bottom_edge += bbox.height();
1090 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1093 yposition = pdz->second;
1096 /* values are zero or negative, hence the use of min() */
1097 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1102 /* The TimeAxisView that this region is now over */
1103 TimeAxisView* current_tv = _time_axis_views[track_index];
1105 /* Ensure it is moved from stacked -> expanded if appropriate */
1106 if (current_tv->view()->layer_display() == Stacked) {
1107 current_tv->view()->set_layer_display (Expanded);
1110 /* We're only allowed to go -ve in layer on Expanded views */
1111 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1112 this_delta_layer = - i->layer;
1116 rv->set_height (current_tv->view()->child_height ());
1118 /* Update show/hidden status as the region view may have come from a hidden track,
1119 or have moved to one.
1121 if (current_tv->hidden ()) {
1122 rv->get_canvas_group()->hide ();
1124 rv->get_canvas_group()->show ();
1127 /* Update the DraggingView */
1128 i->time_axis_view = track_index;
1129 i->layer += this_delta_layer;
1132 _editor->mouse_brush_insert_region (rv, pending_region_position);
1136 /* Get the y coordinate of the top of the track that this region is now over */
1137 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1139 /* And adjust for the layer that it should be on */
1140 StreamView* cv = current_tv->view ();
1141 switch (cv->layer_display ()) {
1145 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1148 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1152 /* need to get the parent of the regionview
1153 * canvas group and get its position in
1154 * equivalent coordinate space as the trackview
1155 * we are now dragging over.
1158 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1163 /* Now move the region view */
1164 rv->move (x_delta, y_delta);
1166 } /* foreach region */
1168 _total_x_delta += x_delta;
1170 if (x_delta != 0 && !_brushing) {
1171 show_verbose_cursor_time (_last_frame_position);
1174 /* keep track of pointer movement */
1176 /* the pointer is currently over a time axis view */
1178 if (_last_pointer_time_axis_view < 0) {
1179 /* last motion event was not over a time axis view
1180 * or last y-movement out of the dropzone was not valid
1183 if (delta_time_axis_view < 0) {
1184 /* in the drop zone, moving up */
1186 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1187 * We do not use negative _last_pointer_time_axis_view because
1188 * the dropzone is "packed" (the actual track offset is ignored)
1190 * As opposed to the actual number
1191 * of elements in the dropzone (_ndropzone)
1192 * _pdropzone is not constrained. This is necessary
1193 * to allow moving multiple regions with y-distance
1196 * There can be 0 elements in the dropzone,
1197 * even though the drag-pointer is inside the DZ.
1200 * [ Audio-track, Midi-track, Audio-track, DZ ]
1201 * move regions from both audio tracks at the same time into the
1202 * DZ by grabbing the region in the bottom track.
1204 assert(current_pointer_time_axis_view >= 0);
1205 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1209 /* only move out of the zone if the movement is OK */
1210 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1211 assert(delta_time_axis_view < 0);
1212 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1213 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1214 * the current position can be calculated as follows:
1216 // a well placed oofus attack can still throw this off.
1217 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1218 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1221 /* last motion event was also over a time axis view */
1222 _last_pointer_time_axis_view += delta_time_axis_view;
1223 assert(_last_pointer_time_axis_view >= 0);
1228 /* the pointer is not over a time axis view */
1229 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1230 _pdropzone += delta_time_axis_view - delta_skip;
1231 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1234 _last_pointer_layer += delta_layer;
1238 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1240 if (_copy && first_move) {
1241 if (_x_constrained && !_brushing) {
1242 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1243 } else if (!_brushing) {
1244 _editor->begin_reversible_command (Operations::region_copy);
1245 } else if (_brushing) {
1246 _editor->begin_reversible_command (Operations::drag_region_brush);
1248 /* duplicate the regionview(s) and region(s) */
1250 list<DraggingView> new_regionviews;
1252 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1254 RegionView* rv = i->view;
1255 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1256 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1258 const boost::shared_ptr<const Region> original = rv->region();
1259 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1260 region_copy->set_position (original->position(), _editor->get_grid_music_divisions (event->button.state));
1261 /* need to set this so that the drop zone code can work. This doesn't
1262 actually put the region into the playlist, but just sets a weak pointer
1265 region_copy->set_playlist (original->playlist());
1269 boost::shared_ptr<AudioRegion> audioregion_copy
1270 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1272 nrv = new AudioRegionView (*arv, audioregion_copy);
1274 boost::shared_ptr<MidiRegion> midiregion_copy
1275 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1276 nrv = new MidiRegionView (*mrv, midiregion_copy);
1281 nrv->get_canvas_group()->show ();
1282 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1284 /* swap _primary to the copy */
1286 if (rv == _primary) {
1290 /* ..and deselect the one we copied */
1292 rv->set_selected (false);
1295 if (!new_regionviews.empty()) {
1297 /* reflect the fact that we are dragging the copies */
1299 _views = new_regionviews;
1301 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1304 } else if (!_copy && first_move) {
1305 if (_x_constrained && !_brushing) {
1306 _editor->begin_reversible_command (_("fixed time region drag"));
1307 } else if (!_brushing) {
1308 _editor->begin_reversible_command (Operations::region_drag);
1309 } else if (_brushing) {
1310 _editor->begin_reversible_command (Operations::drag_region_brush);
1313 RegionMotionDrag::motion (event, first_move);
1317 RegionMotionDrag::finished (GdkEvent *, bool)
1319 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1320 if (!(*i)->view()) {
1324 if ((*i)->view()->layer_display() == Expanded) {
1325 (*i)->view()->set_layer_display (Stacked);
1331 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1333 RegionMotionDrag::finished (ev, movement_occurred);
1335 if (!movement_occurred) {
1339 if (was_double_click() && !_views.empty()) {
1340 DraggingView dv = _views.front();
1341 dv.view->show_region_editor ();
1348 assert (!_views.empty ());
1350 /* We might have hidden region views so that they weren't visible during the drag
1351 (when they have been reparented). Now everything can be shown again, as region
1352 views are back in their track parent groups.
1354 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1355 i->view->get_canvas_group()->show ();
1358 bool const changed_position = (_last_frame_position != _primary->region()->position());
1359 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1360 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1382 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1386 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1388 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1393 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1394 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1395 uint32_t output_chan = region->n_channels();
1396 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1397 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1399 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1400 TimeAxisView* tav =_editor->axis_view_from_stripable (audio_tracks.front());
1402 tav->set_height (original->current_height());
1404 return dynamic_cast<RouteTimeAxisView*>(tav);
1406 ChanCount one_midi_port (DataType::MIDI, 1);
1407 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1408 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1409 (ARDOUR::Plugin::PresetRecord*) 0,
1410 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1411 TimeAxisView* tav = _editor->axis_view_from_stripable (midi_tracks.front());
1413 tav->set_height (original->current_height());
1415 return dynamic_cast<RouteTimeAxisView*> (tav);
1418 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1424 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta, int32_t const ev_state)
1426 RegionSelection new_views;
1427 PlaylistSet modified_playlists;
1428 RouteTimeAxisView* new_time_axis_view = 0;
1431 /* all changes were made during motion event handlers */
1433 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1437 _editor->commit_reversible_command ();
1441 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1442 PlaylistMapping playlist_mapping;
1444 /* insert the regions into their new playlists */
1445 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1447 RouteTimeAxisView* dest_rtv = 0;
1449 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1455 if (changed_position && !_x_constrained) {
1456 where = i->view->region()->position() - drag_delta;
1458 where = i->view->region()->position();
1461 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1462 /* dragged to drop zone */
1464 PlaylistMapping::iterator pm;
1466 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1467 /* first region from this original playlist: create a new track */
1468 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1469 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1470 dest_rtv = new_time_axis_view;
1472 /* we already created a new track for regions from this playlist, use it */
1473 dest_rtv = pm->second;
1476 /* destination time axis view is the one we dragged to */
1477 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1480 if (dest_rtv != 0) {
1481 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1482 if (new_view != 0) {
1483 new_views.push_back (new_view);
1487 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1488 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1491 list<DraggingView>::const_iterator next = i;
1497 /* If we've created new regions either by copying or moving
1498 to a new track, we want to replace the old selection with the new ones
1501 if (new_views.size() > 0) {
1502 _editor->selection->set (new_views);
1505 /* write commands for the accumulated diffs for all our modified playlists */
1506 add_stateful_diff_commands_for_playlists (modified_playlists);
1508 _editor->commit_reversible_command ();
1512 RegionMoveDrag::finished_no_copy (
1513 bool const changed_position,
1514 bool const changed_tracks,
1515 framecnt_t const drag_delta,
1516 int32_t const ev_state
1519 RegionSelection new_views;
1520 PlaylistSet modified_playlists;
1521 PlaylistSet frozen_playlists;
1522 set<RouteTimeAxisView*> views_to_update;
1523 RouteTimeAxisView* new_time_axis_view = 0;
1525 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1526 PlaylistMapping playlist_mapping;
1528 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1530 RegionView* rv = i->view;
1531 RouteTimeAxisView* dest_rtv = 0;
1533 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1538 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1539 /* dragged to drop zone */
1541 PlaylistMapping::iterator pm;
1543 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1544 /* first region from this original playlist: create a new track */
1545 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1546 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1547 dest_rtv = new_time_axis_view;
1549 /* we already created a new track for regions from this playlist, use it */
1550 dest_rtv = pm->second;
1554 /* destination time axis view is the one we dragged to */
1555 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1560 double const dest_layer = i->layer;
1562 views_to_update.insert (dest_rtv);
1566 if (changed_position && !_x_constrained) {
1567 where = rv->region()->position() - drag_delta;
1569 where = rv->region()->position();
1572 if (changed_tracks) {
1574 /* insert into new playlist */
1576 RegionView* new_view = insert_region_into_playlist (
1577 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1580 if (new_view == 0) {
1585 new_views.push_back (new_view);
1587 /* remove from old playlist */
1589 /* the region that used to be in the old playlist is not
1590 moved to the new one - we use a copy of it. as a result,
1591 any existing editor for the region should no longer be
1594 rv->hide_region_editor();
1597 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1601 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1603 /* this movement may result in a crossfade being modified, or a layering change,
1604 so we need to get undo data from the playlist as well as the region.
1607 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1609 playlist->clear_changes ();
1612 rv->region()->clear_changes ();
1615 motion on the same track. plonk the previously reparented region
1616 back to its original canvas group (its streamview).
1617 No need to do anything for copies as they are fake regions which will be deleted.
1620 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1621 rv->get_canvas_group()->set_y_position (i->initial_y);
1624 /* just change the model */
1625 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1626 playlist->set_layer (rv->region(), dest_layer);
1629 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1631 r = frozen_playlists.insert (playlist);
1634 playlist->freeze ();
1637 rv->region()->set_position (where, _editor->get_grid_music_divisions (ev_state));
1638 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1641 if (changed_tracks) {
1643 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1644 was selected in all of them, then removing it from a playlist will have removed all
1645 trace of it from _views (i.e. there were N regions selected, we removed 1,
1646 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1647 corresponding regionview, and _views is now empty).
1649 This could have invalidated any and all iterators into _views.
1651 The heuristic we use here is: if the region selection is empty, break out of the loop
1652 here. if the region selection is not empty, then restart the loop because we know that
1653 we must have removed at least the region(view) we've just been working on as well as any
1654 that we processed on previous iterations.
1656 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1657 we can just iterate.
1661 if (_views.empty()) {
1672 /* If we've created new regions either by copying or moving
1673 to a new track, we want to replace the old selection with the new ones
1676 if (new_views.size() > 0) {
1677 _editor->selection->set (new_views);
1680 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1684 /* write commands for the accumulated diffs for all our modified playlists */
1685 add_stateful_diff_commands_for_playlists (modified_playlists);
1686 /* applies to _brushing */
1687 _editor->commit_reversible_command ();
1689 /* We have futzed with the layering of canvas items on our streamviews.
1690 If any region changed layer, this will have resulted in the stream
1691 views being asked to set up their region views, and all will be well.
1692 If not, we might now have badly-ordered region views. Ask the StreamViews
1693 involved to sort themselves out, just in case.
1696 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1697 (*i)->view()->playlist_layered ((*i)->track ());
1701 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1702 * @param region Region to remove.
1703 * @param playlist playlist To remove from.
1704 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1705 * that clear_changes () is only called once per playlist.
1708 RegionMoveDrag::remove_region_from_playlist (
1709 boost::shared_ptr<Region> region,
1710 boost::shared_ptr<Playlist> playlist,
1711 PlaylistSet& modified_playlists
1714 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1717 playlist->clear_changes ();
1720 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1724 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1725 * clearing the playlist's diff history first if necessary.
1726 * @param region Region to insert.
1727 * @param dest_rtv Destination RouteTimeAxisView.
1728 * @param dest_layer Destination layer.
1729 * @param where Destination position.
1730 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1731 * that clear_changes () is only called once per playlist.
1732 * @return New RegionView, or 0 if no insert was performed.
1735 RegionMoveDrag::insert_region_into_playlist (
1736 boost::shared_ptr<Region> region,
1737 RouteTimeAxisView* dest_rtv,
1740 PlaylistSet& modified_playlists
1743 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1744 if (!dest_playlist) {
1748 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1749 _new_region_view = 0;
1750 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1752 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1753 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1755 dest_playlist->clear_changes ();
1758 dest_playlist->add_region (region, where);
1760 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1761 dest_playlist->set_layer (region, dest_layer);
1766 assert (_new_region_view);
1768 return _new_region_view;
1772 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1774 _new_region_view = rv;
1778 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1780 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1781 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1783 _editor->session()->add_command (c);
1792 RegionMoveDrag::aborted (bool movement_occurred)
1796 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1797 list<DraggingView>::const_iterator next = i;
1806 RegionMotionDrag::aborted (movement_occurred);
1811 RegionMotionDrag::aborted (bool)
1813 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1815 StreamView* sview = (*i)->view();
1818 if (sview->layer_display() == Expanded) {
1819 sview->set_layer_display (Stacked);
1824 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1825 RegionView* rv = i->view;
1826 TimeAxisView* tv = &(rv->get_time_axis_view ());
1827 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1829 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1830 rv->get_canvas_group()->set_y_position (0);
1832 rv->move (-_total_x_delta, 0);
1833 rv->set_height (rtv->view()->child_height ());
1837 /** @param b true to brush, otherwise false.
1838 * @param c true to make copies of the regions being moved, otherwise false.
1840 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1841 : RegionMotionDrag (e, i, p, v, b)
1843 , _new_region_view (0)
1845 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1848 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1849 if (rtv && rtv->is_track()) {
1850 speed = rtv->track()->speed ();
1853 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1857 RegionMoveDrag::setup_pointer_frame_offset ()
1859 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1862 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1863 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1865 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1867 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1868 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1870 _primary = v->view()->create_region_view (r, false, false);
1872 _primary->get_canvas_group()->show ();
1873 _primary->set_position (pos, 0);
1874 _views.push_back (DraggingView (_primary, this, v));
1876 _last_frame_position = pos;
1878 _item = _primary->get_canvas_group ();
1882 RegionInsertDrag::finished (GdkEvent * event, bool)
1884 int pos = _views.front().time_axis_view;
1885 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1887 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1889 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1890 _primary->get_canvas_group()->set_y_position (0);
1892 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1894 _editor->begin_reversible_command (Operations::insert_region);
1895 playlist->clear_changes ();
1896 playlist->add_region (_primary->region (), _last_frame_position);
1898 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1899 if (Config->get_edit_mode() == Ripple) {
1900 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1903 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1904 _editor->commit_reversible_command ();
1912 RegionInsertDrag::aborted (bool)
1919 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1920 : RegionMoveDrag (e, i, p, v, false, false)
1922 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1925 struct RegionSelectionByPosition {
1926 bool operator() (RegionView*a, RegionView* b) {
1927 return a->region()->position () < b->region()->position();
1932 RegionSpliceDrag::motion (GdkEvent* event, bool)
1934 /* Which trackview is this ? */
1936 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1937 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1939 /* The region motion is only processed if the pointer is over
1943 if (!tv || !tv->is_track()) {
1944 /* To make sure we hide the verbose canvas cursor when the mouse is
1945 not held over an audio track.
1947 _editor->verbose_cursor()->hide ();
1950 _editor->verbose_cursor()->show ();
1955 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1961 RegionSelection copy;
1962 _editor->selection->regions.by_position(copy);
1964 framepos_t const pf = adjusted_current_frame (event);
1966 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1968 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1974 boost::shared_ptr<Playlist> playlist;
1976 if ((playlist = atv->playlist()) == 0) {
1980 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1985 if (pf < (*i)->region()->last_frame() + 1) {
1989 if (pf > (*i)->region()->first_frame()) {
1995 playlist->shuffle ((*i)->region(), dir);
2000 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2002 RegionMoveDrag::finished (event, movement_occurred);
2006 RegionSpliceDrag::aborted (bool)
2016 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2019 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2021 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2022 RegionSelection to_ripple;
2023 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2024 if ((*i)->position() >= where) {
2025 to_ripple.push_back (rtv->view()->find_view(*i));
2029 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2030 if (!exclude.contains (*i)) {
2031 // the selection has already been added to _views
2033 if (drag_in_progress) {
2034 // do the same things that RegionMotionDrag::motion does when
2035 // first_move is true, for the region views that we're adding
2036 // to _views this time
2039 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2040 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2041 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2042 rvg->reparent (_editor->_drag_motion_group);
2044 // we only need to move in the y direction
2045 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2050 _views.push_back (DraggingView (*i, this, tav));
2056 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2059 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2060 // we added all the regions after the selection
2062 std::list<DraggingView>::iterator to_erase = i++;
2063 if (!_editor->selection->regions.contains (to_erase->view)) {
2064 // restore the non-selected regions to their original playlist & positions,
2065 // and then ripple them back by the length of the regions that were dragged away
2066 // do the same things as RegionMotionDrag::aborted
2068 RegionView *rv = to_erase->view;
2069 TimeAxisView* tv = &(rv->get_time_axis_view ());
2070 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2073 // plonk them back onto their own track
2074 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2075 rv->get_canvas_group()->set_y_position (0);
2079 // move the underlying region to match the view
2080 rv->region()->set_position (rv->region()->position() + amount);
2082 // restore the view to match the underlying region's original position
2083 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2086 rv->set_height (rtv->view()->child_height ());
2087 _views.erase (to_erase);
2093 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2095 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2097 return allow_moves_across_tracks;
2105 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2106 : RegionMoveDrag (e, i, p, v, false, false)
2108 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2109 // compute length of selection
2110 RegionSelection selected_regions = _editor->selection->regions;
2111 selection_length = selected_regions.end_frame() - selected_regions.start();
2113 // we'll only allow dragging to another track in ripple mode if all the regions
2114 // being dragged start off on the same track
2115 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2118 exclude = new RegionList;
2119 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2120 exclude->push_back((*i)->region());
2123 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2124 RegionSelection copy;
2125 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2127 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2128 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2130 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2131 // find ripple start point on each applicable playlist
2132 RegionView *first_selected_on_this_track = NULL;
2133 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2134 if ((*i)->region()->playlist() == (*pi)) {
2135 // region is on this playlist - it's the first, because they're sorted
2136 first_selected_on_this_track = *i;
2140 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2141 add_all_after_to_views (
2142 &first_selected_on_this_track->get_time_axis_view(),
2143 first_selected_on_this_track->region()->position(),
2144 selected_regions, false);
2147 if (allow_moves_across_tracks) {
2148 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2156 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2158 /* Which trackview is this ? */
2160 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2161 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2163 /* The region motion is only processed if the pointer is over
2167 if (!tv || !tv->is_track()) {
2168 /* To make sure we hide the verbose canvas cursor when the mouse is
2169 not held over an audiotrack.
2171 _editor->verbose_cursor()->hide ();
2175 framepos_t where = adjusted_current_frame (event);
2176 assert (where >= 0);
2178 double delta = compute_x_delta (event, &after);
2180 framecnt_t amount = _editor->pixel_to_sample (delta);
2182 if (allow_moves_across_tracks) {
2183 // all the originally selected regions were on the same track
2185 framecnt_t adjust = 0;
2186 if (prev_tav && tv != prev_tav) {
2187 // dragged onto a different track
2188 // remove the unselected regions from _views, restore them to their original positions
2189 // and add the regions after the drop point on the new playlist to _views instead.
2190 // undo the effect of rippling the previous playlist, and include the effect of removing
2191 // the dragged region(s) from this track
2193 remove_unselected_from_views (prev_amount, false);
2194 // ripple previous playlist according to the regions that have been removed onto the new playlist
2195 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2198 // move just the selected regions
2199 RegionMoveDrag::motion(event, first_move);
2201 // ensure that the ripple operation on the new playlist inserts selection_length time
2202 adjust = selection_length;
2203 // ripple the new current playlist
2204 tv->playlist()->ripple (where, amount+adjust, exclude);
2206 // add regions after point where drag entered this track to subsequent ripples
2207 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2210 // motion on same track
2211 RegionMoveDrag::motion(event, first_move);
2215 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2216 prev_position = where;
2218 // selection encompasses multiple tracks - just drag
2219 // cross-track drags are forbidden
2220 RegionMoveDrag::motion(event, first_move);
2223 if (!_x_constrained) {
2224 prev_amount += amount;
2227 _last_frame_position = after;
2231 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2233 if (!movement_occurred) {
2237 if (was_double_click() && !_views.empty()) {
2238 DraggingView dv = _views.front();
2239 dv.view->show_region_editor ();
2246 _editor->begin_reversible_command(_("Ripple drag"));
2248 // remove the regions being rippled from the dragging view, updating them to
2249 // their new positions
2250 remove_unselected_from_views (prev_amount, true);
2252 if (allow_moves_across_tracks) {
2254 // if regions were dragged across tracks, we've rippled any later
2255 // regions on the track the regions were dragged off, so we need
2256 // to add the original track to the undo record
2257 orig_tav->playlist()->clear_changes();
2258 vector<Command*> cmds;
2259 orig_tav->playlist()->rdiff (cmds);
2260 _editor->session()->add_commands (cmds);
2262 if (prev_tav && prev_tav != orig_tav) {
2263 prev_tav->playlist()->clear_changes();
2264 vector<Command*> cmds;
2265 prev_tav->playlist()->rdiff (cmds);
2266 _editor->session()->add_commands (cmds);
2269 // selection spanned multiple tracks - all will need adding to undo record
2271 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2272 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2274 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2275 (*pi)->clear_changes();
2276 vector<Command*> cmds;
2277 (*pi)->rdiff (cmds);
2278 _editor->session()->add_commands (cmds);
2282 // other modified playlists are added to undo by RegionMoveDrag::finished()
2283 RegionMoveDrag::finished (event, movement_occurred);
2284 _editor->commit_reversible_command();
2288 RegionRippleDrag::aborted (bool movement_occurred)
2290 RegionMoveDrag::aborted (movement_occurred);
2295 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2297 _view (dynamic_cast<MidiTimeAxisView*> (v))
2299 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2305 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2308 _editor->begin_reversible_command (_("create region"));
2309 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2310 _view->playlist()->freeze ();
2313 framepos_t const f = adjusted_current_frame (event);
2314 if (f < grab_frame()) {
2315 _region->set_initial_position (f);
2318 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2319 so that if this region is duplicated, its duplicate starts on
2320 a snap point rather than 1 frame after a snap point. Otherwise things get
2321 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2322 place snapped notes at the start of the region.
2325 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2326 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2332 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2334 if (!movement_occurred) {
2335 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2337 _view->playlist()->thaw ();
2338 _editor->commit_reversible_command();
2343 RegionCreateDrag::aborted (bool)
2346 _view->playlist()->thaw ();
2352 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2357 , _was_selected (false)
2360 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2364 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2366 Gdk::Cursor* cursor;
2367 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2369 float x_fraction = cnote->mouse_x_fraction ();
2371 if (x_fraction > 0.0 && x_fraction < 0.25) {
2372 cursor = _editor->cursors()->left_side_trim;
2375 cursor = _editor->cursors()->right_side_trim;
2379 Drag::start_grab (event, cursor);
2381 region = &cnote->region_view();
2384 temp = region->snap_to_pixel (cnote->x0 (), true);
2385 _snap_delta = temp - cnote->x0 ();
2389 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2394 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2395 if (ms.size() > 1) {
2396 /* has to be relative, may make no sense otherwise */
2400 if (!(_was_selected = cnote->selected())) {
2402 /* tertiary-click means extend selection - we'll do that on button release,
2403 so don't add it here, because otherwise we make it hard to figure
2404 out the "extend-to" range.
2407 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2410 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2413 region->note_selected (cnote, true);
2415 _editor->get_selection().clear_points();
2416 region->unique_select (cnote);
2423 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2425 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2427 _editor->begin_reversible_command (_("resize notes"));
2429 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2430 MidiRegionSelection::iterator next;
2433 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2435 mrv->begin_resizing (at_front);
2441 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2442 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2444 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2448 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2450 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2451 if (_editor->snap_mode () != SnapOff) {
2455 if (_editor->snap_mode () == SnapOff) {
2457 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2458 if (apply_snap_delta) {
2464 if (apply_snap_delta) {
2468 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2474 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2476 if (!movement_occurred) {
2477 /* no motion - select note */
2478 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2479 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2480 _editor->current_mouse_mode() == Editing::MouseDraw) {
2482 bool changed = false;
2484 if (_was_selected) {
2485 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2487 region->note_deselected (cnote);
2490 _editor->get_selection().clear_points();
2491 region->unique_select (cnote);
2495 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2496 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2498 if (!extend && !add && region->selection_size() > 1) {
2499 _editor->get_selection().clear_points();
2500 region->unique_select (cnote);
2502 } else if (extend) {
2503 region->note_selected (cnote, true, true);
2506 /* it was added during button press */
2512 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2513 _editor->commit_reversible_selection_op();
2520 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2521 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2522 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2524 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2527 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2529 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2530 if (_editor->snap_mode () != SnapOff) {
2534 if (_editor->snap_mode () == SnapOff) {
2536 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2537 if (apply_snap_delta) {
2543 if (apply_snap_delta) {
2547 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2551 _editor->commit_reversible_command ();
2555 NoteResizeDrag::aborted (bool)
2557 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2558 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2559 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2561 mrv->abort_resizing ();
2566 AVDraggingView::AVDraggingView (RegionView* v)
2569 initial_position = v->region()->position ();
2572 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2575 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2578 TrackViewList empty;
2580 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2581 std::list<RegionView*> views = rs.by_layer();
2584 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2585 RegionView* rv = (*i);
2586 if (!rv->region()->video_locked()) {
2589 if (rv->region()->locked()) {
2592 _views.push_back (AVDraggingView (rv));
2597 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2599 Drag::start_grab (event);
2600 if (_editor->session() == 0) {
2604 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2610 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2614 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2615 _max_backwards_drag = (
2616 ARDOUR_UI::instance()->video_timeline->get_duration()
2617 + ARDOUR_UI::instance()->video_timeline->get_offset()
2618 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2621 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2622 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2623 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2626 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2629 Timecode::Time timecode;
2630 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2631 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);
2632 show_verbose_cursor_text (buf);
2636 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2638 if (_editor->session() == 0) {
2641 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2645 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2649 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2650 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2652 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2653 dt = - _max_backwards_drag;
2656 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2657 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2659 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2660 RegionView* rv = i->view;
2661 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2664 rv->region()->clear_changes ();
2665 rv->region()->suspend_property_changes();
2667 rv->region()->set_position(i->initial_position + dt);
2668 rv->region_changed(ARDOUR::Properties::position);
2671 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2672 Timecode::Time timecode;
2673 Timecode::Time timediff;
2675 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2676 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2677 snprintf (buf, sizeof (buf),
2678 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2679 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2680 , _("Video Start:"),
2681 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2683 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2685 show_verbose_cursor_text (buf);
2689 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2691 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2698 if (!movement_occurred || ! _editor->session()) {
2702 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2704 _editor->begin_reversible_command (_("Move Video"));
2706 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2707 ARDOUR_UI::instance()->video_timeline->save_undo();
2708 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2709 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2711 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2712 i->view->drag_end();
2713 i->view->region()->resume_property_changes ();
2715 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2718 _editor->session()->maybe_update_session_range(
2719 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2720 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2724 _editor->commit_reversible_command ();
2728 VideoTimeLineDrag::aborted (bool)
2730 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2733 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2734 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2736 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2737 i->view->region()->resume_property_changes ();
2738 i->view->region()->set_position(i->initial_position);
2742 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2743 : RegionDrag (e, i, p, v)
2744 , _operation (StartTrim)
2745 , _preserve_fade_anchor (preserve_fade_anchor)
2746 , _jump_position_when_done (false)
2748 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2752 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2755 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2756 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2758 if (tv && tv->is_track()) {
2759 speed = tv->track()->speed();
2762 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2763 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2764 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2766 framepos_t const pf = adjusted_current_frame (event);
2767 setup_snap_delta (region_start);
2769 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2770 /* Move the contents of the region around without changing the region bounds */
2771 _operation = ContentsTrim;
2772 Drag::start_grab (event, _editor->cursors()->trimmer);
2774 /* These will get overridden for a point trim.*/
2775 if (pf < (region_start + region_length/2)) {
2776 /* closer to front */
2777 _operation = StartTrim;
2778 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2779 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2781 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2785 _operation = EndTrim;
2786 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2787 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2789 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2793 /* jump trim disabled for now
2794 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2795 _jump_position_when_done = true;
2799 switch (_operation) {
2801 show_verbose_cursor_time (region_start);
2804 show_verbose_cursor_duration (region_start, region_end);
2807 show_verbose_cursor_time (pf);
2811 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2812 i->view->region()->suspend_property_changes ();
2817 TrimDrag::motion (GdkEvent* event, bool first_move)
2819 RegionView* rv = _primary;
2822 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2823 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2824 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2825 frameoffset_t frame_delta = 0;
2827 if (tv && tv->is_track()) {
2828 speed = tv->track()->speed();
2830 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2831 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2837 switch (_operation) {
2839 trim_type = "Region start trim";
2842 trim_type = "Region end trim";
2845 trim_type = "Region content trim";
2852 _editor->begin_reversible_command (trim_type);
2854 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2855 RegionView* rv = i->view;
2856 rv->enable_display (false);
2857 rv->region()->playlist()->clear_owned_changes ();
2859 if (_operation == StartTrim) {
2860 rv->trim_front_starting ();
2863 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2866 arv->temporarily_hide_envelope ();
2870 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2871 insert_result = _editor->motion_frozen_playlists.insert (pl);
2873 if (insert_result.second) {
2879 bool non_overlap_trim = false;
2881 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2882 non_overlap_trim = true;
2885 /* contstrain trim to fade length */
2886 if (_preserve_fade_anchor) {
2887 switch (_operation) {
2889 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2890 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2892 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2893 if (ar->locked()) continue;
2894 framecnt_t len = ar->fade_in()->back()->when;
2895 if (len < dt) dt = min(dt, len);
2899 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2900 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2902 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2903 if (ar->locked()) continue;
2904 framecnt_t len = ar->fade_out()->back()->when;
2905 if (len < -dt) dt = max(dt, -len);
2914 switch (_operation) {
2916 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2917 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2918 , _editor->get_grid_music_divisions (event->button.state));
2920 if (changed && _preserve_fade_anchor) {
2921 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2923 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2924 framecnt_t len = ar->fade_in()->back()->when;
2925 framecnt_t diff = ar->first_frame() - i->initial_position;
2926 framepos_t new_length = len - diff;
2927 i->anchored_fade_length = min (ar->length(), new_length);
2928 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2929 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2936 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2937 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, _editor->get_grid_music_divisions (event->button.state));
2938 if (changed && _preserve_fade_anchor) {
2939 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2941 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2942 framecnt_t len = ar->fade_out()->back()->when;
2943 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2944 framepos_t new_length = len + diff;
2945 i->anchored_fade_length = min (ar->length(), new_length);
2946 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2947 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2955 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2957 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2958 i->view->move_contents (frame_delta);
2964 switch (_operation) {
2966 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2969 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2972 // show_verbose_cursor_time (frame_delta);
2978 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2980 if (movement_occurred) {
2981 motion (event, false);
2983 if (_operation == StartTrim) {
2984 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2986 /* This must happen before the region's StatefulDiffCommand is created, as it may
2987 `correct' (ahem) the region's _start from being negative to being zero. It
2988 needs to be zero in the undo record.
2990 i->view->trim_front_ending ();
2992 if (_preserve_fade_anchor) {
2993 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2995 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2996 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2997 ar->set_fade_in_length(i->anchored_fade_length);
2998 ar->set_fade_in_active(true);
3001 if (_jump_position_when_done) {
3002 i->view->region()->set_position (i->initial_position);
3005 } else if (_operation == EndTrim) {
3006 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3007 if (_preserve_fade_anchor) {
3008 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3010 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3011 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3012 ar->set_fade_out_length(i->anchored_fade_length);
3013 ar->set_fade_out_active(true);
3016 if (_jump_position_when_done) {
3017 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3022 if (!_views.empty()) {
3023 if (_operation == StartTrim) {
3024 _editor->maybe_locate_with_edit_preroll(
3025 _views.begin()->view->region()->position());
3027 if (_operation == EndTrim) {
3028 _editor->maybe_locate_with_edit_preroll(
3029 _views.begin()->view->region()->position() +
3030 _views.begin()->view->region()->length());
3034 if (!_editor->selection->selected (_primary)) {
3035 _primary->thaw_after_trim ();
3038 set<boost::shared_ptr<Playlist> > diffed_playlists;
3040 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3041 i->view->thaw_after_trim ();
3042 i->view->enable_display (true);
3044 /* Trimming one region may affect others on the playlist, so we need
3045 to get undo Commands from the whole playlist rather than just the
3046 region. Use diffed_playlists to make sure we don't diff a given
3047 playlist more than once.
3049 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3050 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3051 vector<Command*> cmds;
3053 _editor->session()->add_commands (cmds);
3054 diffed_playlists.insert (p);
3059 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3063 _editor->motion_frozen_playlists.clear ();
3064 _editor->commit_reversible_command();
3067 /* no mouse movement */
3068 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3069 _editor->point_trim (event, adjusted_current_frame (event));
3073 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3074 i->view->region()->resume_property_changes ();
3079 TrimDrag::aborted (bool movement_occurred)
3081 /* Our motion method is changing model state, so use the Undo system
3082 to cancel. Perhaps not ideal, as this will leave an Undo point
3083 behind which may be slightly odd from the user's point of view.
3088 if (movement_occurred) {
3092 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3093 i->view->region()->resume_property_changes ();
3098 TrimDrag::setup_pointer_frame_offset ()
3100 list<DraggingView>::iterator i = _views.begin ();
3101 while (i != _views.end() && i->view != _primary) {
3105 if (i == _views.end()) {
3109 switch (_operation) {
3111 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3114 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3121 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3124 , _old_snap_type (e->snap_type())
3125 , _old_snap_mode (e->snap_mode())
3128 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3129 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3131 _real_section = &_marker->meter();
3136 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3138 Drag::start_grab (event, cursor);
3139 show_verbose_cursor_time (adjusted_current_frame(event));
3143 MeterMarkerDrag::setup_pointer_frame_offset ()
3145 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3149 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3152 // create a dummy marker to catch events, then hide it.
3155 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3157 _marker = new MeterMarker (
3159 *_editor->meter_group,
3160 UIConfiguration::instance().color ("meter marker"),
3162 *new MeterSection (_marker->meter())
3165 /* use the new marker for the grab */
3166 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3169 TempoMap& map (_editor->session()->tempo_map());
3170 /* get current state */
3171 before_state = &map.get_state();
3174 _editor->begin_reversible_command (_("move meter mark"));
3176 _editor->begin_reversible_command (_("copy meter mark"));
3178 Timecode::BBT_Time bbt = _real_section->bbt();
3180 /* we can't add a meter where one currently exists */
3181 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3186 const double beat = map.beat_at_bbt (bbt);
3187 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3188 , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style());
3191 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3192 if (_real_section->position_lock_style() != AudioTime) {
3193 _editor->set_snap_to (SnapToBar);
3194 _editor->set_snap_mode (SnapNormal);
3198 framepos_t pf = adjusted_current_frame (event);
3200 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3201 /* never snap to music for audio locked */
3202 pf = adjusted_current_frame (event, false);
3205 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3207 /* fake marker meeds to stay under the mouse, unlike the real one. */
3208 _marker->set_position (adjusted_current_frame (event, false));
3210 show_verbose_cursor_time (_real_section->frame());
3214 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3216 if (!movement_occurred) {
3217 if (was_double_click()) {
3218 _editor->edit_meter_marker (*_marker);
3223 /* reinstate old snap setting */
3224 _editor->set_snap_to (_old_snap_type);
3225 _editor->set_snap_mode (_old_snap_mode);
3227 TempoMap& map (_editor->session()->tempo_map());
3229 XMLNode &after = map.get_state();
3230 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3231 _editor->commit_reversible_command ();
3233 // delete the dummy marker we used for visual representation while moving.
3234 // a new visual marker will show up automatically.
3239 MeterMarkerDrag::aborted (bool moved)
3241 _marker->set_position (_marker->meter().frame ());
3243 /* reinstate old snap setting */
3244 _editor->set_snap_to (_old_snap_type);
3245 _editor->set_snap_mode (_old_snap_mode);
3247 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3248 // delete the dummy marker we used for visual representation while moving.
3249 // a new visual marker will show up automatically.
3254 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3259 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3261 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3262 _real_section = &_marker->tempo();
3263 _movable = _real_section->movable();
3268 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3270 Drag::start_grab (event, cursor);
3271 if (!_real_section->active()) {
3272 show_verbose_cursor_text (_("inactive"));
3274 show_verbose_cursor_time (adjusted_current_frame (event));
3279 TempoMarkerDrag::setup_pointer_frame_offset ()
3281 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3285 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3287 if (!_real_section->active()) {
3293 // mvc drag - create a dummy marker to catch events, hide it.
3296 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3298 TempoSection section (_marker->tempo());
3300 _marker = new TempoMarker (
3302 *_editor->tempo_group,
3303 UIConfiguration::instance().color ("tempo marker"),
3305 *new TempoSection (_marker->tempo())
3308 /* use the new marker for the grab */
3309 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3312 TempoMap& map (_editor->session()->tempo_map());
3313 /* get current state */
3314 before_state = &map.get_state();
3317 _editor->begin_reversible_command (_("move tempo mark"));
3320 const framepos_t frame = adjusted_current_frame (event) + 1;
3322 _editor->begin_reversible_command (_("copy tempo mark"));
3324 if (_real_section->position_lock_style() == MusicTime) {
3325 _real_section = map.add_tempo (_marker->tempo(), map.pulse_at_frame (frame), 0, _real_section->type(), MusicTime);
3327 _real_section = map.add_tempo (_marker->tempo(), 0.0, frame, _real_section->type(), AudioTime);
3333 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3334 /* use vertical movement to alter tempo .. should be log */
3335 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3338 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3340 show_verbose_cursor_text (strs.str());
3342 } else if (_movable && !_real_section->locked_to_meter()) {
3343 const framepos_t pf = adjusted_current_frame (event);
3344 TempoMap& map (_editor->session()->tempo_map());
3346 /* snap to beat is 1, snap to bar is -1 (sorry) */
3347 int sub_num = _editor->get_grid_music_divisions (event->button.state);
3349 map.gui_move_tempo (_real_section, pf, sub_num);
3351 show_verbose_cursor_time (_real_section->frame());
3353 _marker->set_position (adjusted_current_frame (event, false));
3357 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3359 if (!_real_section->active()) {
3362 if (!movement_occurred) {
3363 if (was_double_click()) {
3364 _editor->edit_tempo_marker (*_marker);
3369 TempoMap& map (_editor->session()->tempo_map());
3371 XMLNode &after = map.get_state();
3372 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3373 _editor->commit_reversible_command ();
3375 // delete the dummy marker we used for visual representation while moving.
3376 // a new visual marker will show up automatically.
3381 TempoMarkerDrag::aborted (bool moved)
3383 _marker->set_position (_marker->tempo().frame());
3385 TempoMap& map (_editor->session()->tempo_map());
3386 map.set_state (*before_state, Stateful::current_state_version);
3387 // delete the dummy (hidden) marker we used for events while moving.
3392 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3398 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3403 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3405 Drag::start_grab (event, cursor);
3406 TempoMap& map (_editor->session()->tempo_map());
3407 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3410 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3411 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3412 show_verbose_cursor_text (sstr.str());
3413 finished (event, false);
3417 BBTRulerDrag::setup_pointer_frame_offset ()
3419 TempoMap& map (_editor->session()->tempo_map());
3420 const double beat_at_frame = map.beat_at_frame (raw_grab_frame());
3421 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3424 if (divisions > 0) {
3425 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3427 /* while it makes some sense for the user to determine the division to 'grab',
3428 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3429 and the result over steep tempo curves. Use sixteenths.
3431 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3434 _pulse = map.pulse_at_beat (beat);
3436 _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3441 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3443 TempoMap& map (_editor->session()->tempo_map());
3446 /* get current state */
3447 before_state = &map.get_state();
3448 _editor->begin_reversible_command (_("dilate tempo"));
3453 if (_editor->snap_musical()) {
3454 pf = adjusted_current_frame (event, false);
3456 pf = adjusted_current_frame (event);
3459 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3460 /* adjust previous tempo to match pointer frame */
3461 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3464 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3465 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3466 show_verbose_cursor_text (sstr.str());
3470 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3472 if (!movement_occurred) {
3476 TempoMap& map (_editor->session()->tempo_map());
3478 XMLNode &after = map.get_state();
3479 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3480 _editor->commit_reversible_command ();
3484 BBTRulerDrag::aborted (bool moved)
3487 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3492 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3493 : Drag (e, &c.track_canvas_item(), false)
3498 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3501 /** Do all the things we do when dragging the playhead to make it look as though
3502 * we have located, without actually doing the locate (because that would cause
3503 * the diskstream buffers to be refilled, which is too slow).
3506 CursorDrag::fake_locate (framepos_t t)
3508 if (_editor->session () == 0) {
3512 _editor->playhead_cursor->set_position (t);
3514 Session* s = _editor->session ();
3515 if (s->timecode_transmission_suspended ()) {
3516 framepos_t const f = _editor->playhead_cursor->current_frame ();
3517 /* This is asynchronous so it will be sent "now"
3519 s->send_mmc_locate (f);
3520 /* These are synchronous and will be sent during the next
3523 s->queue_full_time_code ();
3524 s->queue_song_position_pointer ();
3527 show_verbose_cursor_time (t);
3528 _editor->UpdateAllTransportClocks (t);
3532 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3534 Drag::start_grab (event, c);
3535 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3537 _grab_zoom = _editor->samples_per_pixel;
3539 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3541 _editor->snap_to_with_modifier (where, event);
3543 _editor->_dragging_playhead = true;
3545 Session* s = _editor->session ();
3547 /* grab the track canvas item as well */
3549 _cursor.track_canvas_item().grab();
3552 if (_was_rolling && _stop) {
3556 if (s->is_auditioning()) {
3557 s->cancel_audition ();
3561 if (AudioEngine::instance()->connected()) {
3563 /* do this only if we're the engine is connected
3564 * because otherwise this request will never be
3565 * serviced and we'll busy wait forever. likewise,
3566 * notice if we are disconnected while waiting for the
3567 * request to be serviced.
3570 s->request_suspend_timecode_transmission ();
3571 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3572 /* twiddle our thumbs */
3577 fake_locate (where - snap_delta (event->button.state));
3581 CursorDrag::motion (GdkEvent* event, bool)
3583 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3584 _editor->snap_to_with_modifier (where, event);
3585 if (where != last_pointer_frame()) {
3586 fake_locate (where - snap_delta (event->button.state));
3591 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3593 _editor->_dragging_playhead = false;
3595 _cursor.track_canvas_item().ungrab();
3597 if (!movement_occurred && _stop) {
3601 motion (event, false);
3603 Session* s = _editor->session ();
3605 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3606 _editor->_pending_locate_request = true;
3607 s->request_resume_timecode_transmission ();
3612 CursorDrag::aborted (bool)
3614 _cursor.track_canvas_item().ungrab();
3616 if (_editor->_dragging_playhead) {
3617 _editor->session()->request_resume_timecode_transmission ();
3618 _editor->_dragging_playhead = false;
3621 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3624 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3625 : RegionDrag (e, i, p, v)
3627 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3631 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3633 Drag::start_grab (event, cursor);
3635 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3636 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3637 setup_snap_delta (r->position ());
3639 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3643 FadeInDrag::setup_pointer_frame_offset ()
3645 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3646 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3647 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3651 FadeInDrag::motion (GdkEvent* event, bool)
3653 framecnt_t fade_length;
3655 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3656 _editor->snap_to_with_modifier (pos, event);
3657 pos -= snap_delta (event->button.state);
3659 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3661 if (pos < (region->position() + 64)) {
3662 fade_length = 64; // this should be a minimum defined somewhere
3663 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3664 fade_length = region->length() - region->fade_out()->back()->when - 1;
3666 fade_length = pos - region->position();
3669 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3671 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3677 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3680 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3684 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3686 if (!movement_occurred) {
3690 framecnt_t fade_length;
3691 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3692 _editor->snap_to_with_modifier (pos, event);
3693 pos -= snap_delta (event->button.state);
3695 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3697 if (pos < (region->position() + 64)) {
3698 fade_length = 64; // this should be a minimum defined somewhere
3699 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3700 fade_length = region->length() - region->fade_out()->back()->when - 1;
3702 fade_length = pos - region->position();
3705 bool in_command = false;
3707 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3709 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3715 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3716 XMLNode &before = alist->get_state();
3718 tmp->audio_region()->set_fade_in_length (fade_length);
3719 tmp->audio_region()->set_fade_in_active (true);
3722 _editor->begin_reversible_command (_("change fade in length"));
3725 XMLNode &after = alist->get_state();
3726 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3730 _editor->commit_reversible_command ();
3735 FadeInDrag::aborted (bool)
3737 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3738 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3744 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3748 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3749 : RegionDrag (e, i, p, v)
3751 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3755 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3757 Drag::start_grab (event, cursor);
3759 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3760 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3761 setup_snap_delta (r->last_frame ());
3763 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3767 FadeOutDrag::setup_pointer_frame_offset ()
3769 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3770 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3771 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3775 FadeOutDrag::motion (GdkEvent* event, bool)
3777 framecnt_t fade_length;
3779 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3780 _editor->snap_to_with_modifier (pos, event);
3781 pos -= snap_delta (event->button.state);
3783 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3785 if (pos > (region->last_frame() - 64)) {
3786 fade_length = 64; // this should really be a minimum fade defined somewhere
3787 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3788 fade_length = region->length() - region->fade_in()->back()->when - 1;
3790 fade_length = region->last_frame() - pos;
3793 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3795 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3801 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3804 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3808 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3810 if (!movement_occurred) {
3814 framecnt_t fade_length;
3816 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3817 _editor->snap_to_with_modifier (pos, event);
3818 pos -= snap_delta (event->button.state);
3820 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3822 if (pos > (region->last_frame() - 64)) {
3823 fade_length = 64; // this should really be a minimum fade defined somewhere
3824 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3825 fade_length = region->length() - region->fade_in()->back()->when - 1;
3827 fade_length = region->last_frame() - pos;
3830 bool in_command = false;
3832 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3834 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3840 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3841 XMLNode &before = alist->get_state();
3843 tmp->audio_region()->set_fade_out_length (fade_length);
3844 tmp->audio_region()->set_fade_out_active (true);
3847 _editor->begin_reversible_command (_("change fade out length"));
3850 XMLNode &after = alist->get_state();
3851 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3855 _editor->commit_reversible_command ();
3860 FadeOutDrag::aborted (bool)
3862 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3863 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3869 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3873 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3875 , _selection_changed (false)
3877 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3878 Gtk::Window* toplevel = _editor->current_toplevel();
3879 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3883 _points.push_back (ArdourCanvas::Duple (0, 0));
3885 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3888 MarkerDrag::~MarkerDrag ()
3890 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3895 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3897 location = new Location (*l);
3898 markers.push_back (m);
3903 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3905 Drag::start_grab (event, cursor);
3909 Location *location = _editor->find_location_from_marker (_marker, is_start);
3910 _editor->_dragging_edit_point = true;
3912 update_item (location);
3914 // _drag_line->show();
3915 // _line->raise_to_top();
3918 show_verbose_cursor_time (location->start());
3920 show_verbose_cursor_time (location->end());
3922 setup_snap_delta (is_start ? location->start() : location->end());
3924 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3927 case Selection::Toggle:
3928 /* we toggle on the button release */
3930 case Selection::Set:
3931 if (!_editor->selection->selected (_marker)) {
3932 _editor->selection->set (_marker);
3933 _selection_changed = true;
3936 case Selection::Extend:
3938 Locations::LocationList ll;
3939 list<ArdourMarker*> to_add;
3941 _editor->selection->markers.range (s, e);
3942 s = min (_marker->position(), s);
3943 e = max (_marker->position(), e);
3946 if (e < max_framepos) {
3949 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3950 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3951 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3954 to_add.push_back (lm->start);
3957 to_add.push_back (lm->end);
3961 if (!to_add.empty()) {
3962 _editor->selection->add (to_add);
3963 _selection_changed = true;
3967 case Selection::Add:
3968 _editor->selection->add (_marker);
3969 _selection_changed = true;
3974 /* Set up copies for us to manipulate during the drag
3977 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3979 Location* l = _editor->find_location_from_marker (*i, is_start);
3986 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3988 /* range: check that the other end of the range isn't
3991 CopiedLocationInfo::iterator x;
3992 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3993 if (*(*x).location == *l) {
3997 if (x == _copied_locations.end()) {
3998 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4000 (*x).markers.push_back (*i);
4001 (*x).move_both = true;
4009 MarkerDrag::setup_pointer_frame_offset ()
4012 Location *location = _editor->find_location_from_marker (_marker, is_start);
4013 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4017 MarkerDrag::motion (GdkEvent* event, bool)
4019 framecnt_t f_delta = 0;
4021 bool move_both = false;
4022 Location *real_location;
4023 Location *copy_location = 0;
4024 framecnt_t const sd = snap_delta (event->button.state);
4026 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4027 framepos_t next = newframe;
4029 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4033 CopiedLocationInfo::iterator x;
4035 /* find the marker we're dragging, and compute the delta */
4037 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4039 copy_location = (*x).location;
4041 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4043 /* this marker is represented by this
4044 * CopiedLocationMarkerInfo
4047 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4052 if (real_location->is_mark()) {
4053 f_delta = newframe - copy_location->start();
4057 switch (_marker->type()) {
4058 case ArdourMarker::SessionStart:
4059 case ArdourMarker::RangeStart:
4060 case ArdourMarker::LoopStart:
4061 case ArdourMarker::PunchIn:
4062 f_delta = newframe - copy_location->start();
4065 case ArdourMarker::SessionEnd:
4066 case ArdourMarker::RangeEnd:
4067 case ArdourMarker::LoopEnd:
4068 case ArdourMarker::PunchOut:
4069 f_delta = newframe - copy_location->end();
4072 /* what kind of marker is this ? */
4081 if (x == _copied_locations.end()) {
4082 /* hmm, impossible - we didn't find the dragged marker */
4086 /* now move them all */
4088 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4090 copy_location = x->location;
4092 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4096 if (real_location->locked()) {
4100 if (copy_location->is_mark()) {
4104 copy_location->set_start (copy_location->start() + f_delta);
4108 framepos_t new_start = copy_location->start() + f_delta;
4109 framepos_t new_end = copy_location->end() + f_delta;
4111 if (is_start) { // start-of-range marker
4113 if (move_both || (*x).move_both) {
4114 copy_location->set_start (new_start);
4115 copy_location->set_end (new_end);
4116 } else if (new_start < copy_location->end()) {
4117 copy_location->set_start (new_start);
4118 } else if (newframe > 0) {
4119 //_editor->snap_to (next, RoundUpAlways, true);
4120 copy_location->set_end (next);
4121 copy_location->set_start (newframe);
4124 } else { // end marker
4126 if (move_both || (*x).move_both) {
4127 copy_location->set_end (new_end);
4128 copy_location->set_start (new_start);
4129 } else if (new_end > copy_location->start()) {
4130 copy_location->set_end (new_end);
4131 } else if (newframe > 0) {
4132 //_editor->snap_to (next, RoundDownAlways, true);
4133 copy_location->set_start (next);
4134 copy_location->set_end (newframe);
4139 update_item (copy_location);
4141 /* now lookup the actual GUI items used to display this
4142 * location and move them to wherever the copy of the location
4143 * is now. This means that the logic in ARDOUR::Location is
4144 * still enforced, even though we are not (yet) modifying
4145 * the real Location itself.
4148 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4151 lm->set_position (copy_location->start(), copy_location->end());
4156 assert (!_copied_locations.empty());
4158 show_verbose_cursor_time (newframe);
4162 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4164 if (!movement_occurred) {
4166 if (was_double_click()) {
4167 _editor->rename_marker (_marker);
4171 /* just a click, do nothing but finish
4172 off the selection process
4175 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4177 case Selection::Set:
4178 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4179 _editor->selection->set (_marker);
4180 _selection_changed = true;
4184 case Selection::Toggle:
4185 /* we toggle on the button release, click only */
4186 _editor->selection->toggle (_marker);
4187 _selection_changed = true;
4191 case Selection::Extend:
4192 case Selection::Add:
4196 if (_selection_changed) {
4197 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4198 _editor->commit_reversible_selection_op();
4204 _editor->_dragging_edit_point = false;
4206 XMLNode &before = _editor->session()->locations()->get_state();
4207 bool in_command = false;
4209 MarkerSelection::iterator i;
4210 CopiedLocationInfo::iterator x;
4213 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4214 x != _copied_locations.end() && i != _editor->selection->markers.end();
4217 Location * location = _editor->find_location_from_marker (*i, is_start);
4221 if (location->locked()) {
4225 _editor->begin_reversible_command ( _("move marker") );
4228 if (location->is_mark()) {
4229 location->set_start (((*x).location)->start());
4231 location->set (((*x).location)->start(), ((*x).location)->end());
4237 XMLNode &after = _editor->session()->locations()->get_state();
4238 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4239 _editor->commit_reversible_command ();
4244 MarkerDrag::aborted (bool movement_occurred)
4246 if (!movement_occurred) {
4250 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4252 /* move all markers to their original location */
4255 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4258 Location * location = _editor->find_location_from_marker (*m, is_start);
4261 (*m)->set_position (is_start ? location->start() : location->end());
4268 MarkerDrag::update_item (Location*)
4273 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4275 , _fixed_grab_x (0.0)
4276 , _fixed_grab_y (0.0)
4277 , _cumulative_x_drag (0.0)
4278 , _cumulative_y_drag (0.0)
4282 if (_zero_gain_fraction < 0.0) {
4283 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4286 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4288 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4294 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4296 Drag::start_grab (event, _editor->cursors()->fader);
4298 // start the grab at the center of the control point so
4299 // the point doesn't 'jump' to the mouse after the first drag
4300 _fixed_grab_x = _point->get_x();
4301 _fixed_grab_y = _point->get_y();
4303 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4304 setup_snap_delta (pos);
4306 float const fraction = 1 - (_point->get_y() / _point->line().height());
4307 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4309 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4311 if (!_point->can_slide ()) {
4312 _x_constrained = true;
4317 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4319 double dx = _drags->current_pointer_x() - last_pointer_x();
4320 double dy = current_pointer_y() - last_pointer_y();
4321 bool need_snap = true;
4323 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4329 /* coordinate in pixels relative to the start of the region (for region-based automation)
4330 or track (for track-based automation) */
4331 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4332 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4334 // calculate zero crossing point. back off by .01 to stay on the
4335 // positive side of zero
4336 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4338 if (_x_constrained) {
4341 if (_y_constrained) {
4345 _cumulative_x_drag = cx - _fixed_grab_x;
4346 _cumulative_y_drag = cy - _fixed_grab_y;
4350 cy = min ((double) _point->line().height(), cy);
4352 // make sure we hit zero when passing through
4353 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4357 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4359 if (!_x_constrained && need_snap) {
4360 _editor->snap_to_with_modifier (cx_frames, event);
4363 cx_frames -= snap_delta (event->button.state);
4364 cx_frames = min (cx_frames, _point->line().maximum_time());
4366 float const fraction = 1.0 - (cy / _point->line().height());
4369 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4370 _editor->begin_reversible_command (_("automation event move"));
4371 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4373 pair<double, float> result;
4374 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4376 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4380 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4382 if (!movement_occurred) {
4385 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4386 _editor->reset_point_selection ();
4390 _point->line().end_drag (_pushing, _final_index);
4391 _editor->commit_reversible_command ();
4396 ControlPointDrag::aborted (bool)
4398 _point->line().reset ();
4402 ControlPointDrag::active (Editing::MouseMode m)
4404 if (m == Editing::MouseDraw) {
4405 /* always active in mouse draw */
4409 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4410 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4413 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4416 , _fixed_grab_x (0.0)
4417 , _fixed_grab_y (0.0)
4418 , _cumulative_y_drag (0)
4422 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4426 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4428 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4431 _item = &_line->grab_item ();
4433 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4434 origin, and ditto for y.
4437 double mx = event->button.x;
4438 double my = event->button.y;
4440 _line->grab_item().canvas_to_item (mx, my);
4442 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4444 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4445 /* no adjacent points */
4449 Drag::start_grab (event, _editor->cursors()->fader);
4451 /* store grab start in item frame */
4452 double const bx = _line->nth (_before)->get_x();
4453 double const ax = _line->nth (_after)->get_x();
4454 double const click_ratio = (ax - mx) / (ax - bx);
4456 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4461 double fraction = 1.0 - (cy / _line->height());
4463 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4467 LineDrag::motion (GdkEvent* event, bool first_move)
4469 double dy = current_pointer_y() - last_pointer_y();
4471 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4475 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4477 _cumulative_y_drag = cy - _fixed_grab_y;
4480 cy = min ((double) _line->height(), cy);
4482 double const fraction = 1.0 - (cy / _line->height());
4486 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4488 _editor->begin_reversible_command (_("automation range move"));
4489 _line->start_drag_line (_before, _after, initial_fraction);
4492 /* we are ignoring x position for this drag, so we can just pass in anything */
4493 pair<double, float> result;
4495 result = _line->drag_motion (0, fraction, true, false, ignored);
4496 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4500 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4502 if (movement_occurred) {
4503 motion (event, false);
4504 _line->end_drag (false, 0);
4505 _editor->commit_reversible_command ();
4507 /* add a new control point on the line */
4509 AutomationTimeAxisView* atv;
4511 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4512 framepos_t where = grab_frame ();
4515 double cy = _fixed_grab_y;
4517 _line->grab_item().item_to_canvas (cx, cy);
4519 atv->add_automation_event (event, where, cy, false);
4520 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4521 AudioRegionView* arv;
4523 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4524 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4531 LineDrag::aborted (bool)
4536 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4540 _region_view_grab_x (0.0),
4541 _cumulative_x_drag (0),
4545 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4549 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4551 Drag::start_grab (event);
4553 _line = reinterpret_cast<Line*> (_item);
4556 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4558 double cx = event->button.x;
4559 double cy = event->button.y;
4561 _item->parent()->canvas_to_item (cx, cy);
4563 /* store grab start in parent frame */
4564 _region_view_grab_x = cx;
4566 _before = *(float*) _item->get_data ("position");
4568 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4570 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4574 FeatureLineDrag::motion (GdkEvent*, bool)
4576 double dx = _drags->current_pointer_x() - last_pointer_x();
4578 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4580 _cumulative_x_drag += dx;
4582 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4591 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4593 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4595 float *pos = new float;
4598 _line->set_data ("position", pos);
4604 FeatureLineDrag::finished (GdkEvent*, bool)
4606 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4607 _arv->update_transient(_before, _before);
4611 FeatureLineDrag::aborted (bool)
4616 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4618 , _vertical_only (false)
4620 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4624 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4626 Drag::start_grab (event);
4627 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4631 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4638 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4640 framepos_t grab = grab_frame ();
4641 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4642 _editor->snap_to_with_modifier (grab, event);
4644 grab = raw_grab_frame ();
4647 /* base start and end on initial click position */
4657 if (current_pointer_y() < grab_y()) {
4658 y1 = current_pointer_y();
4661 y2 = current_pointer_y();
4665 if (start != end || y1 != y2) {
4667 double x1 = _editor->sample_to_pixel (start);
4668 double x2 = _editor->sample_to_pixel (end);
4669 const double min_dimension = 2.0;
4671 if (_vertical_only) {
4672 /* fixed 10 pixel width */
4676 x2 = min (x1 - min_dimension, x2);
4678 x2 = max (x1 + min_dimension, x2);
4683 y2 = min (y1 - min_dimension, y2);
4685 y2 = max (y1 + min_dimension, y2);
4688 /* translate rect into item space and set */
4690 ArdourCanvas::Rect r (x1, y1, x2, y2);
4692 /* this drag is a _trackview_only == true drag, so the y1 and
4693 * y2 (computed using current_pointer_y() and grab_y()) will be
4694 * relative to the top of the trackview group). The
4695 * rubberband rect has the same parent/scroll offset as the
4696 * the trackview group, so we can use the "r" rect directly
4697 * to set the shape of the rubberband.
4700 _editor->rubberband_rect->set (r);
4701 _editor->rubberband_rect->show();
4702 _editor->rubberband_rect->raise_to_top();
4704 show_verbose_cursor_time (pf);
4706 do_select_things (event, true);
4711 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4715 framepos_t grab = grab_frame ();
4716 framepos_t lpf = last_pointer_frame ();
4718 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4719 grab = raw_grab_frame ();
4720 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4734 if (current_pointer_y() < grab_y()) {
4735 y1 = current_pointer_y();
4738 y2 = current_pointer_y();
4742 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4746 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4748 if (movement_occurred) {
4750 motion (event, false);
4751 do_select_things (event, false);
4757 bool do_deselect = true;
4758 MidiTimeAxisView* mtv;
4760 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4762 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4763 /* nothing selected */
4764 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4765 do_deselect = false;
4769 /* do not deselect if Primary or Tertiary (toggle-select or
4770 * extend-select are pressed.
4773 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4774 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4781 _editor->rubberband_rect->hide();
4785 RubberbandSelectDrag::aborted (bool)
4787 _editor->rubberband_rect->hide ();
4790 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4791 : RegionDrag (e, i, p, v)
4793 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4797 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4799 Drag::start_grab (event, cursor);
4801 _editor->get_selection().add (_primary);
4803 framepos_t where = _primary->region()->position();
4804 setup_snap_delta (where);
4806 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4810 TimeFXDrag::motion (GdkEvent* event, bool)
4812 RegionView* rv = _primary;
4813 StreamView* cv = rv->get_time_axis_view().view ();
4815 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4816 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4817 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4818 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4819 _editor->snap_to_with_modifier (pf, event);
4820 pf -= snap_delta (event->button.state);
4822 if (pf > rv->region()->position()) {
4823 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4826 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4830 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4832 /* this may have been a single click, no drag. We still want the dialog
4833 to show up in that case, so that the user can manually edit the
4834 parameters for the timestretch.
4837 float fraction = 1.0;
4839 if (movement_occurred) {
4841 motion (event, false);
4843 _primary->get_time_axis_view().hide_timestretch ();
4845 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4847 if (adjusted_frame_pos < _primary->region()->position()) {
4848 /* backwards drag of the left edge - not usable */
4852 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4854 fraction = (double) newlen / (double) _primary->region()->length();
4856 #ifndef USE_RUBBERBAND
4857 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4858 if (_primary->region()->data_type() == DataType::AUDIO) {
4859 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4864 if (!_editor->get_selection().regions.empty()) {
4865 /* primary will already be included in the selection, and edit
4866 group shared editing will propagate selection across
4867 equivalent regions, so just use the current region
4871 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4872 error << _("An error occurred while executing time stretch operation") << endmsg;
4878 TimeFXDrag::aborted (bool)
4880 _primary->get_time_axis_view().hide_timestretch ();
4883 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4886 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4890 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4892 Drag::start_grab (event);
4896 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4898 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4902 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4904 if (movement_occurred && _editor->session()) {
4905 /* make sure we stop */
4906 _editor->session()->request_transport_speed (0.0);
4911 ScrubDrag::aborted (bool)
4916 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4920 , _time_selection_at_start (!_editor->get_selection().time.empty())
4922 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4924 if (_time_selection_at_start) {
4925 start_at_start = _editor->get_selection().time.start();
4926 end_at_start = _editor->get_selection().time.end_frame();
4931 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4933 if (_editor->session() == 0) {
4937 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4939 switch (_operation) {
4940 case CreateSelection:
4941 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4946 cursor = _editor->cursors()->selector;
4947 Drag::start_grab (event, cursor);
4950 case SelectionStartTrim:
4951 if (_editor->clicked_axisview) {
4952 _editor->clicked_axisview->order_selection_trims (_item, true);
4954 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4957 case SelectionEndTrim:
4958 if (_editor->clicked_axisview) {
4959 _editor->clicked_axisview->order_selection_trims (_item, false);
4961 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4965 Drag::start_grab (event, cursor);
4968 case SelectionExtend:
4969 Drag::start_grab (event, cursor);
4973 if (_operation == SelectionMove) {
4974 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4976 show_verbose_cursor_time (adjusted_current_frame (event));
4981 SelectionDrag::setup_pointer_frame_offset ()
4983 switch (_operation) {
4984 case CreateSelection:
4985 _pointer_frame_offset = 0;
4988 case SelectionStartTrim:
4990 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4993 case SelectionEndTrim:
4994 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4997 case SelectionExtend:
5003 SelectionDrag::motion (GdkEvent* event, bool first_move)
5005 framepos_t start = 0;
5007 framecnt_t length = 0;
5008 framecnt_t distance = 0;
5010 framepos_t const pending_position = adjusted_current_frame (event);
5012 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5016 switch (_operation) {
5017 case CreateSelection:
5019 framepos_t grab = grab_frame ();
5022 grab = adjusted_current_frame (event, false);
5023 if (grab < pending_position) {
5024 _editor->snap_to (grab, RoundDownMaybe);
5026 _editor->snap_to (grab, RoundUpMaybe);
5030 if (pending_position < grab) {
5031 start = pending_position;
5034 end = pending_position;
5038 /* first drag: Either add to the selection
5039 or create a new selection
5046 /* adding to the selection */
5047 _editor->set_selected_track_as_side_effect (Selection::Add);
5048 _editor->clicked_selection = _editor->selection->add (start, end);
5055 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5056 _editor->set_selected_track_as_side_effect (Selection::Set);
5059 _editor->clicked_selection = _editor->selection->set (start, end);
5063 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5064 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5065 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5067 _editor->selection->add (atest);
5071 /* select all tracks within the rectangle that we've marked out so far */
5072 TrackViewList new_selection;
5073 TrackViewList& all_tracks (_editor->track_views);
5075 ArdourCanvas::Coord const top = grab_y();
5076 ArdourCanvas::Coord const bottom = current_pointer_y();
5078 if (top >= 0 && bottom >= 0) {
5080 //first, find the tracks that are covered in the y range selection
5081 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5082 if ((*i)->covered_by_y_range (top, bottom)) {
5083 new_selection.push_back (*i);
5087 //now find any tracks that are GROUPED with the tracks we selected
5088 TrackViewList grouped_add = new_selection;
5089 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5090 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5091 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5092 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5093 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5094 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5095 grouped_add.push_back (*j);
5100 //now compare our list with the current selection, and add or remove as necessary
5101 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5102 TrackViewList tracks_to_add;
5103 TrackViewList tracks_to_remove;
5104 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5105 if ( !_editor->selection->tracks.contains ( *i ) )
5106 tracks_to_add.push_back ( *i );
5107 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5108 if ( !grouped_add.contains ( *i ) )
5109 tracks_to_remove.push_back ( *i );
5110 _editor->selection->add(tracks_to_add);
5111 _editor->selection->remove(tracks_to_remove);
5117 case SelectionStartTrim:
5119 end = _editor->selection->time[_editor->clicked_selection].end;
5121 if (pending_position > end) {
5124 start = pending_position;
5128 case SelectionEndTrim:
5130 start = _editor->selection->time[_editor->clicked_selection].start;
5132 if (pending_position < start) {
5135 end = pending_position;
5142 start = _editor->selection->time[_editor->clicked_selection].start;
5143 end = _editor->selection->time[_editor->clicked_selection].end;
5145 length = end - start;
5146 distance = pending_position - start;
5147 start = pending_position;
5148 _editor->snap_to (start);
5150 end = start + length;
5154 case SelectionExtend:
5159 switch (_operation) {
5161 if (_time_selection_at_start) {
5162 _editor->selection->move_time (distance);
5166 _editor->selection->replace (_editor->clicked_selection, start, end);
5170 if (_operation == SelectionMove) {
5171 show_verbose_cursor_time(start);
5173 show_verbose_cursor_time(pending_position);
5178 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5180 Session* s = _editor->session();
5182 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5183 if (movement_occurred) {
5184 motion (event, false);
5185 /* XXX this is not object-oriented programming at all. ick */
5186 if (_editor->selection->time.consolidate()) {
5187 _editor->selection->TimeChanged ();
5190 /* XXX what if its a music time selection? */
5192 if (s->get_play_range() && s->transport_rolling()) {
5193 s->request_play_range (&_editor->selection->time, true);
5194 } else if (!s->config.get_external_sync()) {
5195 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5196 if (_operation == SelectionEndTrim)
5197 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5199 s->request_locate (_editor->get_selection().time.start());
5203 if (_editor->get_selection().time.length() != 0) {
5204 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5206 s->clear_range_selection ();
5211 /* just a click, no pointer movement.
5214 if (_operation == SelectionExtend) {
5215 if (_time_selection_at_start) {
5216 framepos_t pos = adjusted_current_frame (event, false);
5217 framepos_t start = min (pos, start_at_start);
5218 framepos_t end = max (pos, end_at_start);
5219 _editor->selection->set (start, end);
5222 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5223 if (_editor->clicked_selection) {
5224 _editor->selection->remove (_editor->clicked_selection);
5227 if (!_editor->clicked_selection) {
5228 _editor->selection->clear_time();
5233 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5234 _editor->selection->set (_editor->clicked_axisview);
5237 if (s && s->get_play_range () && s->transport_rolling()) {
5238 s->request_stop (false, false);
5243 _editor->stop_canvas_autoscroll ();
5244 _editor->clicked_selection = 0;
5245 _editor->commit_reversible_selection_op ();
5249 SelectionDrag::aborted (bool)
5254 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5255 : Drag (e, i, false),
5259 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5261 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5262 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5263 physical_screen_height (_editor->current_toplevel()->get_window())));
5264 _drag_rect->hide ();
5266 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5267 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5270 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5272 /* normal canvas items will be cleaned up when their parent group is deleted. But
5273 this item is created as the child of a long-lived parent group, and so we
5274 need to explicitly delete it.
5280 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5282 if (_editor->session() == 0) {
5286 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5288 if (!_editor->temp_location) {
5289 _editor->temp_location = new Location (*_editor->session());
5292 switch (_operation) {
5293 case CreateSkipMarker:
5294 case CreateRangeMarker:
5295 case CreateTransportMarker:
5296 case CreateCDMarker:
5298 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5303 cursor = _editor->cursors()->selector;
5307 Drag::start_grab (event, cursor);
5309 show_verbose_cursor_time (adjusted_current_frame (event));
5313 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5315 framepos_t start = 0;
5317 ArdourCanvas::Rectangle *crect;
5319 switch (_operation) {
5320 case CreateSkipMarker:
5321 crect = _editor->range_bar_drag_rect;
5323 case CreateRangeMarker:
5324 crect = _editor->range_bar_drag_rect;
5326 case CreateTransportMarker:
5327 crect = _editor->transport_bar_drag_rect;
5329 case CreateCDMarker:
5330 crect = _editor->cd_marker_bar_drag_rect;
5333 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5338 framepos_t const pf = adjusted_current_frame (event);
5340 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5341 framepos_t grab = grab_frame ();
5342 _editor->snap_to (grab);
5344 if (pf < grab_frame()) {
5352 /* first drag: Either add to the selection
5353 or create a new selection.
5358 _editor->temp_location->set (start, end);
5362 update_item (_editor->temp_location);
5364 //_drag_rect->raise_to_top();
5370 _editor->temp_location->set (start, end);
5372 double x1 = _editor->sample_to_pixel (start);
5373 double x2 = _editor->sample_to_pixel (end);
5377 update_item (_editor->temp_location);
5380 show_verbose_cursor_time (pf);
5385 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5387 Location * newloc = 0;
5391 if (movement_occurred) {
5392 motion (event, false);
5395 switch (_operation) {
5396 case CreateSkipMarker:
5397 case CreateRangeMarker:
5398 case CreateCDMarker:
5400 XMLNode &before = _editor->session()->locations()->get_state();
5401 if (_operation == CreateSkipMarker) {
5402 _editor->begin_reversible_command (_("new skip marker"));
5403 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5404 flags = Location::IsRangeMarker | Location::IsSkip;
5405 _editor->range_bar_drag_rect->hide();
5406 } else if (_operation == CreateCDMarker) {
5407 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5408 _editor->begin_reversible_command (_("new CD marker"));
5409 flags = Location::IsRangeMarker | Location::IsCDMarker;
5410 _editor->cd_marker_bar_drag_rect->hide();
5412 _editor->begin_reversible_command (_("new skip marker"));
5413 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5414 flags = Location::IsRangeMarker;
5415 _editor->range_bar_drag_rect->hide();
5417 newloc = new Location (
5418 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5421 _editor->session()->locations()->add (newloc, true);
5422 XMLNode &after = _editor->session()->locations()->get_state();
5423 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5424 _editor->commit_reversible_command ();
5428 case CreateTransportMarker:
5429 // popup menu to pick loop or punch
5430 _editor->new_transport_marker_context_menu (&event->button, _item);
5436 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5438 if (_operation == CreateTransportMarker) {
5440 /* didn't drag, so just locate */
5442 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5444 } else if (_operation == CreateCDMarker) {
5446 /* didn't drag, but mark is already created so do
5449 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5454 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5456 if (end == max_framepos) {
5457 end = _editor->session()->current_end_frame ();
5460 if (start == max_framepos) {
5461 start = _editor->session()->current_start_frame ();
5464 switch (_editor->mouse_mode) {
5466 /* find the two markers on either side and then make the selection from it */
5467 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5471 /* find the two markers on either side of the click and make the range out of it */
5472 _editor->selection->set (start, end);
5481 _editor->stop_canvas_autoscroll ();
5485 RangeMarkerBarDrag::aborted (bool movement_occurred)
5487 if (movement_occurred) {
5488 _drag_rect->hide ();
5493 RangeMarkerBarDrag::update_item (Location* location)
5495 double const x1 = _editor->sample_to_pixel (location->start());
5496 double const x2 = _editor->sample_to_pixel (location->end());
5498 _drag_rect->set_x0 (x1);
5499 _drag_rect->set_x1 (x2);
5502 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5504 , _cumulative_dx (0)
5505 , _cumulative_dy (0)
5506 , _was_selected (false)
5508 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5510 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5512 _region = &_primary->region_view ();
5513 _note_height = _region->midi_stream_view()->note_height ();
5517 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5519 Drag::start_grab (event);
5520 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5522 if (!(_was_selected = _primary->selected())) {
5524 /* tertiary-click means extend selection - we'll do that on button release,
5525 so don't add it here, because otherwise we make it hard to figure
5526 out the "extend-to" range.
5529 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5532 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5535 _region->note_selected (_primary, true);
5537 _editor->get_selection().clear_points();
5538 _region->unique_select (_primary);
5544 /** @return Current total drag x change in frames */
5546 NoteDrag::total_dx (const guint state) const
5549 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5551 /* primary note time */
5552 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5554 /* new time of the primary note in session frames */
5555 frameoffset_t st = n + dx + snap_delta (state);
5557 framepos_t const rp = _region->region()->position ();
5559 /* prevent the note being dragged earlier than the region's position */
5562 /* possibly snap and return corresponding delta */
5566 if (ArdourKeyboard::indicates_snap (state)) {
5567 if (_editor->snap_mode () != SnapOff) {
5571 if (_editor->snap_mode () == SnapOff) {
5573 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5574 if (ArdourKeyboard::indicates_snap_delta (state)) {
5582 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5583 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5585 ret = st - n - snap_delta (state);
5590 /** @return Current total drag y change in note number */
5592 NoteDrag::total_dy () const
5594 MidiStreamView* msv = _region->midi_stream_view ();
5595 double const y = _region->midi_view()->y_position ();
5596 /* new current note */
5597 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5599 n = max (msv->lowest_note(), n);
5600 n = min (msv->highest_note(), n);
5601 /* and work out delta */
5602 return n - msv->y_to_note (grab_y() - y);
5606 NoteDrag::motion (GdkEvent * event, bool)
5608 /* Total change in x and y since the start of the drag */
5609 frameoffset_t const dx = total_dx (event->button.state);
5610 int8_t const dy = total_dy ();
5612 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5613 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5614 double const tdy = -dy * _note_height - _cumulative_dy;
5617 _cumulative_dx += tdx;
5618 _cumulative_dy += tdy;
5620 int8_t note_delta = total_dy();
5622 _region->move_selection (tdx, tdy, note_delta);
5624 /* the new note value may be the same as the old one, but we
5625 * don't know what that means because the selection may have
5626 * involved more than one note and we might be doing something
5627 * odd with them. so show the note value anyway, always.
5630 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5632 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5637 NoteDrag::finished (GdkEvent* ev, bool moved)
5640 /* no motion - select note */
5642 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5643 _editor->current_mouse_mode() == Editing::MouseDraw) {
5645 bool changed = false;
5647 if (_was_selected) {
5648 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5650 _region->note_deselected (_primary);
5653 _editor->get_selection().clear_points();
5654 _region->unique_select (_primary);
5658 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5659 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5661 if (!extend && !add && _region->selection_size() > 1) {
5662 _editor->get_selection().clear_points();
5663 _region->unique_select (_primary);
5665 } else if (extend) {
5666 _region->note_selected (_primary, true, true);
5669 /* it was added during button press */
5676 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5677 _editor->commit_reversible_selection_op();
5681 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5686 NoteDrag::aborted (bool)
5691 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5692 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5693 : Drag (editor, atv->base_item ())
5695 , _y_origin (atv->y_position())
5696 , _nothing_to_drag (false)
5698 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5699 setup (atv->lines ());
5702 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5703 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5704 : Drag (editor, rv->get_canvas_group ())
5706 , _y_origin (rv->get_time_axis_view().y_position())
5707 , _nothing_to_drag (false)
5710 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5712 list<boost::shared_ptr<AutomationLine> > lines;
5714 AudioRegionView* audio_view;
5715 AutomationRegionView* automation_view;
5716 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5717 lines.push_back (audio_view->get_gain_line ());
5718 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5719 lines.push_back (automation_view->line ());
5722 error << _("Automation range drag created for invalid region type") << endmsg;
5728 /** @param lines AutomationLines to drag.
5729 * @param offset Offset from the session start to the points in the AutomationLines.
5732 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5734 /* find the lines that overlap the ranges being dragged */
5735 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5736 while (i != lines.end ()) {
5737 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5740 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5742 /* check this range against all the AudioRanges that we are using */
5743 list<AudioRange>::const_iterator k = _ranges.begin ();
5744 while (k != _ranges.end()) {
5745 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5751 /* add it to our list if it overlaps at all */
5752 if (k != _ranges.end()) {
5757 _lines.push_back (n);
5763 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5767 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5769 return 1.0 - ((global_y - _y_origin) / line->height());
5773 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5775 const double v = list->eval(x);
5776 return _integral ? rint(v) : v;
5780 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5782 Drag::start_grab (event, cursor);
5784 /* Get line states before we start changing things */
5785 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5786 i->state = &i->line->get_state ();
5787 i->original_fraction = y_fraction (i->line, current_pointer_y());
5790 if (_ranges.empty()) {
5792 /* No selected time ranges: drag all points */
5793 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5794 uint32_t const N = i->line->npoints ();
5795 for (uint32_t j = 0; j < N; ++j) {
5796 i->points.push_back (i->line->nth (j));
5802 if (_nothing_to_drag) {
5808 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5810 if (_nothing_to_drag && !first_move) {
5815 _editor->begin_reversible_command (_("automation range move"));
5817 if (!_ranges.empty()) {
5819 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5821 framecnt_t const half = (i->start + i->end) / 2;
5823 /* find the line that this audio range starts in */
5824 list<Line>::iterator j = _lines.begin();
5825 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5829 if (j != _lines.end()) {
5830 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5832 /* j is the line that this audio range starts in; fade into it;
5833 64 samples length plucked out of thin air.
5836 framepos_t a = i->start + 64;
5841 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5842 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5844 XMLNode &before = the_list->get_state();
5845 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5846 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5848 if (add_p || add_q) {
5849 _editor->session()->add_command (
5850 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5854 /* same thing for the end */
5857 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5861 if (j != _lines.end()) {
5862 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5864 /* j is the line that this audio range starts in; fade out of it;
5865 64 samples length plucked out of thin air.
5868 framepos_t b = i->end - 64;
5873 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5874 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5876 XMLNode &before = the_list->get_state();
5877 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5878 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5880 if (add_p || add_q) {
5881 _editor->session()->add_command (
5882 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5887 _nothing_to_drag = true;
5889 /* Find all the points that should be dragged and put them in the relevant
5890 points lists in the Line structs.
5893 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5895 uint32_t const N = i->line->npoints ();
5896 for (uint32_t j = 0; j < N; ++j) {
5898 /* here's a control point on this line */
5899 ControlPoint* p = i->line->nth (j);
5900 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5902 /* see if it's inside a range */
5903 list<AudioRange>::const_iterator k = _ranges.begin ();
5904 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5908 if (k != _ranges.end()) {
5909 /* dragging this point */
5910 _nothing_to_drag = false;
5911 i->points.push_back (p);
5917 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5918 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5922 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5923 float const f = y_fraction (l->line, current_pointer_y());
5924 /* we are ignoring x position for this drag, so we can just pass in anything */
5925 pair<double, float> result;
5927 result = l->line->drag_motion (0, f, true, false, ignored);
5928 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5933 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5935 if (_nothing_to_drag || !motion_occurred) {
5939 motion (event, false);
5940 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5941 i->line->end_drag (false, 0);
5944 _editor->commit_reversible_command ();
5948 AutomationRangeDrag::aborted (bool)
5950 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5955 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5957 , initial_time_axis_view (itav)
5959 /* note that time_axis_view may be null if the regionview was created
5960 * as part of a copy operation.
5962 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5963 layer = v->region()->layer ();
5964 initial_y = v->get_canvas_group()->position().y;
5965 initial_playlist = v->region()->playlist ();
5966 initial_position = v->region()->position ();
5967 initial_end = v->region()->position () + v->region()->length ();
5970 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5971 : Drag (e, i->canvas_item ())
5974 , _cumulative_dx (0)
5976 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5977 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5982 PatchChangeDrag::motion (GdkEvent* ev, bool)
5984 framepos_t f = adjusted_current_frame (ev);
5985 boost::shared_ptr<Region> r = _region_view->region ();
5986 f = max (f, r->position ());
5987 f = min (f, r->last_frame ());
5989 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5990 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5991 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5992 _cumulative_dx = dxu;
5996 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5998 if (!movement_occurred) {
5999 if (was_double_click()) {
6000 _region_view->edit_patch_change (_patch_change);
6005 boost::shared_ptr<Region> r (_region_view->region ());
6006 framepos_t f = adjusted_current_frame (ev);
6007 f = max (f, r->position ());
6008 f = min (f, r->last_frame ());
6010 _region_view->move_patch_change (
6012 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6017 PatchChangeDrag::aborted (bool)
6019 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6023 PatchChangeDrag::setup_pointer_frame_offset ()
6025 boost::shared_ptr<Region> region = _region_view->region ();
6026 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6029 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6030 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6037 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6039 _region_view->update_drag_selection (
6041 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6045 MidiRubberbandSelectDrag::deselect_things ()
6050 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6051 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6054 _vertical_only = true;
6058 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6060 double const y = _region_view->midi_view()->y_position ();
6062 y1 = max (0.0, y1 - y);
6063 y2 = max (0.0, y2 - y);
6065 _region_view->update_vertical_drag_selection (
6068 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6073 MidiVerticalSelectDrag::deselect_things ()
6078 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6079 : RubberbandSelectDrag (e, i)
6085 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6087 if (drag_in_progress) {
6088 /* We just want to select things at the end of the drag, not during it */
6092 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6094 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6096 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6098 _editor->commit_reversible_selection_op ();
6102 EditorRubberbandSelectDrag::deselect_things ()
6104 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6106 _editor->selection->clear_tracks();
6107 _editor->selection->clear_regions();
6108 _editor->selection->clear_points ();
6109 _editor->selection->clear_lines ();
6110 _editor->selection->clear_midi_notes ();
6112 _editor->commit_reversible_selection_op();
6115 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6120 _note[0] = _note[1] = 0;
6123 NoteCreateDrag::~NoteCreateDrag ()
6129 NoteCreateDrag::grid_frames (framepos_t t) const
6132 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6134 grid_beats = Evoral::Beats(1);
6137 return _region_view->region_beats_to_region_frames (grid_beats);
6141 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6143 Drag::start_grab (event, cursor);
6145 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6147 framepos_t pf = _drags->current_pointer_frame ();
6148 framecnt_t const g = grid_frames (pf);
6150 /* Hack so that we always snap to the note that we are over, instead of snapping
6151 to the next one if we're more than halfway through the one we're over.
6153 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6157 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6158 _note[1] = _note[0];
6160 MidiStreamView* sv = _region_view->midi_stream_view ();
6161 double const x = _editor->sample_to_pixel (_note[0]);
6162 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6164 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6165 _drag_rect->set_outline_all ();
6166 _drag_rect->set_outline_color (0xffffff99);
6167 _drag_rect->set_fill_color (0xffffff66);
6171 NoteCreateDrag::motion (GdkEvent* event, bool)
6173 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6174 double const x0 = _editor->sample_to_pixel (_note[0]);
6175 double const x1 = _editor->sample_to_pixel (_note[1]);
6176 _drag_rect->set_x0 (std::min(x0, x1));
6177 _drag_rect->set_x1 (std::max(x0, x1));
6181 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6183 if (!had_movement) {
6187 framepos_t const start = min (_note[0], _note[1]);
6188 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6190 framecnt_t const g = grid_frames (start);
6191 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6193 if (_editor->snap_mode() == SnapNormal && length < g) {
6197 Evoral::Beats length_beats = max (
6198 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6200 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6204 NoteCreateDrag::y_to_region (double y) const
6207 _region_view->get_canvas_group()->canvas_to_item (x, y);
6212 NoteCreateDrag::aborted (bool)
6217 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6222 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6226 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6228 Drag::start_grab (event, cursor);
6232 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6238 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6241 distance = _drags->current_pointer_x() - grab_x();
6242 len = ar->fade_in()->back()->when;
6244 distance = grab_x() - _drags->current_pointer_x();
6245 len = ar->fade_out()->back()->when;
6248 /* how long should it be ? */
6250 new_length = len + _editor->pixel_to_sample (distance);
6252 /* now check with the region that this is legal */
6254 new_length = ar->verify_xfade_bounds (new_length, start);
6257 arv->reset_fade_in_shape_width (ar, new_length);
6259 arv->reset_fade_out_shape_width (ar, new_length);
6264 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6270 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6273 distance = _drags->current_pointer_x() - grab_x();
6274 len = ar->fade_in()->back()->when;
6276 distance = grab_x() - _drags->current_pointer_x();
6277 len = ar->fade_out()->back()->when;
6280 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6282 _editor->begin_reversible_command ("xfade trim");
6283 ar->playlist()->clear_owned_changes ();
6286 ar->set_fade_in_length (new_length);
6288 ar->set_fade_out_length (new_length);
6291 /* Adjusting the xfade may affect other regions in the playlist, so we need
6292 to get undo Commands from the whole playlist rather than just the
6296 vector<Command*> cmds;
6297 ar->playlist()->rdiff (cmds);
6298 _editor->session()->add_commands (cmds);
6299 _editor->commit_reversible_command ();
6304 CrossfadeEdgeDrag::aborted (bool)
6307 // arv->redraw_start_xfade ();
6309 // arv->redraw_end_xfade ();
6313 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6314 : Drag (e, item, true)
6315 , line (new EditorCursor (*e))
6317 line->set_position (pos);
6321 RegionCutDrag::~RegionCutDrag ()
6327 RegionCutDrag::motion (GdkEvent*, bool)
6329 framepos_t where = _drags->current_pointer_frame();
6330 _editor->snap_to (where);
6332 line->set_position (where);
6336 RegionCutDrag::finished (GdkEvent* event, bool)
6338 _editor->get_track_canvas()->canvas()->re_enter();
6340 framepos_t pos = _drags->current_pointer_frame();
6344 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6350 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state));
6354 RegionCutDrag::aborted (bool)