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 (max (0.0, 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 , _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,
1482 modified_playlists, _editor->get_grid_music_divisions (ev_state));
1484 if (new_view != 0) {
1485 new_views.push_back (new_view);
1489 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1490 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1493 list<DraggingView>::const_iterator next = i;
1499 /* If we've created new regions either by copying or moving
1500 to a new track, we want to replace the old selection with the new ones
1503 if (new_views.size() > 0) {
1504 _editor->selection->set (new_views);
1507 /* write commands for the accumulated diffs for all our modified playlists */
1508 add_stateful_diff_commands_for_playlists (modified_playlists);
1510 _editor->commit_reversible_command ();
1514 RegionMoveDrag::finished_no_copy (
1515 bool const changed_position,
1516 bool const changed_tracks,
1517 framecnt_t const drag_delta,
1518 int32_t const ev_state
1521 RegionSelection new_views;
1522 PlaylistSet modified_playlists;
1523 PlaylistSet frozen_playlists;
1524 set<RouteTimeAxisView*> views_to_update;
1525 RouteTimeAxisView* new_time_axis_view = 0;
1527 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1528 PlaylistMapping playlist_mapping;
1530 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1532 RegionView* rv = i->view;
1533 RouteTimeAxisView* dest_rtv = 0;
1535 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1540 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1541 /* dragged to drop zone */
1543 PlaylistMapping::iterator pm;
1545 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1546 /* first region from this original playlist: create a new track */
1547 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1548 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1549 dest_rtv = new_time_axis_view;
1551 /* we already created a new track for regions from this playlist, use it */
1552 dest_rtv = pm->second;
1556 /* destination time axis view is the one we dragged to */
1557 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1562 double const dest_layer = i->layer;
1564 views_to_update.insert (dest_rtv);
1568 if (changed_position && !_x_constrained) {
1569 where = rv->region()->position() - drag_delta;
1571 where = rv->region()->position();
1574 if (changed_tracks) {
1576 /* insert into new playlist */
1578 RegionView* new_view = insert_region_into_playlist (
1579 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1580 modified_playlists, _editor->get_grid_music_divisions (ev_state)
1583 if (new_view == 0) {
1588 new_views.push_back (new_view);
1590 /* remove from old playlist */
1592 /* the region that used to be in the old playlist is not
1593 moved to the new one - we use a copy of it. as a result,
1594 any existing editor for the region should no longer be
1597 rv->hide_region_editor();
1600 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1604 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1606 /* this movement may result in a crossfade being modified, or a layering change,
1607 so we need to get undo data from the playlist as well as the region.
1610 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1612 playlist->clear_changes ();
1615 rv->region()->clear_changes ();
1618 motion on the same track. plonk the previously reparented region
1619 back to its original canvas group (its streamview).
1620 No need to do anything for copies as they are fake regions which will be deleted.
1623 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1624 rv->get_canvas_group()->set_y_position (i->initial_y);
1627 /* just change the model */
1628 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1629 playlist->set_layer (rv->region(), dest_layer);
1632 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1634 r = frozen_playlists.insert (playlist);
1637 playlist->freeze ();
1640 rv->region()->set_position (where, _editor->get_grid_music_divisions (ev_state));
1641 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1644 if (changed_tracks) {
1646 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1647 was selected in all of them, then removing it from a playlist will have removed all
1648 trace of it from _views (i.e. there were N regions selected, we removed 1,
1649 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1650 corresponding regionview, and _views is now empty).
1652 This could have invalidated any and all iterators into _views.
1654 The heuristic we use here is: if the region selection is empty, break out of the loop
1655 here. if the region selection is not empty, then restart the loop because we know that
1656 we must have removed at least the region(view) we've just been working on as well as any
1657 that we processed on previous iterations.
1659 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1660 we can just iterate.
1664 if (_views.empty()) {
1675 /* If we've created new regions either by copying or moving
1676 to a new track, we want to replace the old selection with the new ones
1679 if (new_views.size() > 0) {
1680 _editor->selection->set (new_views);
1683 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1687 /* write commands for the accumulated diffs for all our modified playlists */
1688 add_stateful_diff_commands_for_playlists (modified_playlists);
1689 /* applies to _brushing */
1690 _editor->commit_reversible_command ();
1692 /* We have futzed with the layering of canvas items on our streamviews.
1693 If any region changed layer, this will have resulted in the stream
1694 views being asked to set up their region views, and all will be well.
1695 If not, we might now have badly-ordered region views. Ask the StreamViews
1696 involved to sort themselves out, just in case.
1699 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1700 (*i)->view()->playlist_layered ((*i)->track ());
1704 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1705 * @param region Region to remove.
1706 * @param playlist playlist To remove from.
1707 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1708 * that clear_changes () is only called once per playlist.
1711 RegionMoveDrag::remove_region_from_playlist (
1712 boost::shared_ptr<Region> region,
1713 boost::shared_ptr<Playlist> playlist,
1714 PlaylistSet& modified_playlists
1717 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1720 playlist->clear_changes ();
1723 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1727 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1728 * clearing the playlist's diff history first if necessary.
1729 * @param region Region to insert.
1730 * @param dest_rtv Destination RouteTimeAxisView.
1731 * @param dest_layer Destination layer.
1732 * @param where Destination position.
1733 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1734 * that clear_changes () is only called once per playlist.
1735 * @return New RegionView, or 0 if no insert was performed.
1738 RegionMoveDrag::insert_region_into_playlist (
1739 boost::shared_ptr<Region> region,
1740 RouteTimeAxisView* dest_rtv,
1743 PlaylistSet& modified_playlists,
1744 const int32_t sub_num
1747 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1748 if (!dest_playlist) {
1752 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1753 _new_region_view = 0;
1754 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1756 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1757 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1759 dest_playlist->clear_changes ();
1761 dest_playlist->add_region (region, where, 1.0, false, sub_num);
1763 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1764 dest_playlist->set_layer (region, dest_layer);
1769 assert (_new_region_view);
1771 return _new_region_view;
1775 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1777 _new_region_view = rv;
1781 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1783 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1784 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1786 _editor->session()->add_command (c);
1795 RegionMoveDrag::aborted (bool movement_occurred)
1799 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1800 list<DraggingView>::const_iterator next = i;
1809 RegionMotionDrag::aborted (movement_occurred);
1814 RegionMotionDrag::aborted (bool)
1816 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1818 StreamView* sview = (*i)->view();
1821 if (sview->layer_display() == Expanded) {
1822 sview->set_layer_display (Stacked);
1827 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1828 RegionView* rv = i->view;
1829 TimeAxisView* tv = &(rv->get_time_axis_view ());
1830 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1832 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1833 rv->get_canvas_group()->set_y_position (0);
1835 rv->move (-_total_x_delta, 0);
1836 rv->set_height (rtv->view()->child_height ());
1840 /** @param b true to brush, otherwise false.
1841 * @param c true to make copies of the regions being moved, otherwise false.
1843 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1844 : RegionMotionDrag (e, i, p, v, b)
1846 , _new_region_view (0)
1848 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1851 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1852 if (rtv && rtv->is_track()) {
1853 speed = rtv->track()->speed ();
1856 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1860 RegionMoveDrag::setup_pointer_frame_offset ()
1862 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1865 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1866 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1868 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1870 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1871 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1873 _primary = v->view()->create_region_view (r, false, false);
1875 _primary->get_canvas_group()->show ();
1876 _primary->set_position (pos, 0);
1877 _views.push_back (DraggingView (_primary, this, v));
1879 _last_frame_position = pos;
1881 _item = _primary->get_canvas_group ();
1885 RegionInsertDrag::finished (GdkEvent * event, bool)
1887 int pos = _views.front().time_axis_view;
1888 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1890 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1892 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1893 _primary->get_canvas_group()->set_y_position (0);
1895 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1897 _editor->begin_reversible_command (Operations::insert_region);
1898 playlist->clear_changes ();
1899 playlist->add_region (_primary->region (), _last_frame_position);
1901 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1902 if (Config->get_edit_mode() == Ripple) {
1903 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1906 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1907 _editor->commit_reversible_command ();
1915 RegionInsertDrag::aborted (bool)
1922 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1923 : RegionMoveDrag (e, i, p, v, false, false)
1925 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1928 struct RegionSelectionByPosition {
1929 bool operator() (RegionView*a, RegionView* b) {
1930 return a->region()->position () < b->region()->position();
1935 RegionSpliceDrag::motion (GdkEvent* event, bool)
1937 /* Which trackview is this ? */
1939 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1940 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1942 /* The region motion is only processed if the pointer is over
1946 if (!tv || !tv->is_track()) {
1947 /* To make sure we hide the verbose canvas cursor when the mouse is
1948 not held over an audio track.
1950 _editor->verbose_cursor()->hide ();
1953 _editor->verbose_cursor()->show ();
1958 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1964 RegionSelection copy;
1965 _editor->selection->regions.by_position(copy);
1967 framepos_t const pf = adjusted_current_frame (event);
1969 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1971 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1977 boost::shared_ptr<Playlist> playlist;
1979 if ((playlist = atv->playlist()) == 0) {
1983 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1988 if (pf < (*i)->region()->last_frame() + 1) {
1992 if (pf > (*i)->region()->first_frame()) {
1998 playlist->shuffle ((*i)->region(), dir);
2003 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2005 RegionMoveDrag::finished (event, movement_occurred);
2009 RegionSpliceDrag::aborted (bool)
2019 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2022 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2024 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2025 RegionSelection to_ripple;
2026 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2027 if ((*i)->position() >= where) {
2028 to_ripple.push_back (rtv->view()->find_view(*i));
2032 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2033 if (!exclude.contains (*i)) {
2034 // the selection has already been added to _views
2036 if (drag_in_progress) {
2037 // do the same things that RegionMotionDrag::motion does when
2038 // first_move is true, for the region views that we're adding
2039 // to _views this time
2042 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2043 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2044 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2045 rvg->reparent (_editor->_drag_motion_group);
2047 // we only need to move in the y direction
2048 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2053 _views.push_back (DraggingView (*i, this, tav));
2059 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2062 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2063 // we added all the regions after the selection
2065 std::list<DraggingView>::iterator to_erase = i++;
2066 if (!_editor->selection->regions.contains (to_erase->view)) {
2067 // restore the non-selected regions to their original playlist & positions,
2068 // and then ripple them back by the length of the regions that were dragged away
2069 // do the same things as RegionMotionDrag::aborted
2071 RegionView *rv = to_erase->view;
2072 TimeAxisView* tv = &(rv->get_time_axis_view ());
2073 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2076 // plonk them back onto their own track
2077 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2078 rv->get_canvas_group()->set_y_position (0);
2082 // move the underlying region to match the view
2083 rv->region()->set_position (rv->region()->position() + amount);
2085 // restore the view to match the underlying region's original position
2086 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2089 rv->set_height (rtv->view()->child_height ());
2090 _views.erase (to_erase);
2096 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2098 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2100 return allow_moves_across_tracks;
2108 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2109 : RegionMoveDrag (e, i, p, v, false, false)
2111 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2112 // compute length of selection
2113 RegionSelection selected_regions = _editor->selection->regions;
2114 selection_length = selected_regions.end_frame() - selected_regions.start();
2116 // we'll only allow dragging to another track in ripple mode if all the regions
2117 // being dragged start off on the same track
2118 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2121 exclude = new RegionList;
2122 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2123 exclude->push_back((*i)->region());
2126 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2127 RegionSelection copy;
2128 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2130 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2131 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2133 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2134 // find ripple start point on each applicable playlist
2135 RegionView *first_selected_on_this_track = NULL;
2136 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2137 if ((*i)->region()->playlist() == (*pi)) {
2138 // region is on this playlist - it's the first, because they're sorted
2139 first_selected_on_this_track = *i;
2143 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2144 add_all_after_to_views (
2145 &first_selected_on_this_track->get_time_axis_view(),
2146 first_selected_on_this_track->region()->position(),
2147 selected_regions, false);
2150 if (allow_moves_across_tracks) {
2151 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2159 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2161 /* Which trackview is this ? */
2163 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2164 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2166 /* The region motion is only processed if the pointer is over
2170 if (!tv || !tv->is_track()) {
2171 /* To make sure we hide the verbose canvas cursor when the mouse is
2172 not held over an audiotrack.
2174 _editor->verbose_cursor()->hide ();
2178 framepos_t where = adjusted_current_frame (event);
2179 assert (where >= 0);
2181 double delta = compute_x_delta (event, &after);
2183 framecnt_t amount = _editor->pixel_to_sample (delta);
2185 if (allow_moves_across_tracks) {
2186 // all the originally selected regions were on the same track
2188 framecnt_t adjust = 0;
2189 if (prev_tav && tv != prev_tav) {
2190 // dragged onto a different track
2191 // remove the unselected regions from _views, restore them to their original positions
2192 // and add the regions after the drop point on the new playlist to _views instead.
2193 // undo the effect of rippling the previous playlist, and include the effect of removing
2194 // the dragged region(s) from this track
2196 remove_unselected_from_views (prev_amount, false);
2197 // ripple previous playlist according to the regions that have been removed onto the new playlist
2198 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2201 // move just the selected regions
2202 RegionMoveDrag::motion(event, first_move);
2204 // ensure that the ripple operation on the new playlist inserts selection_length time
2205 adjust = selection_length;
2206 // ripple the new current playlist
2207 tv->playlist()->ripple (where, amount+adjust, exclude);
2209 // add regions after point where drag entered this track to subsequent ripples
2210 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2213 // motion on same track
2214 RegionMoveDrag::motion(event, first_move);
2218 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2219 prev_position = where;
2221 // selection encompasses multiple tracks - just drag
2222 // cross-track drags are forbidden
2223 RegionMoveDrag::motion(event, first_move);
2226 if (!_x_constrained) {
2227 prev_amount += amount;
2230 _last_frame_position = after;
2234 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2236 if (!movement_occurred) {
2240 if (was_double_click() && !_views.empty()) {
2241 DraggingView dv = _views.front();
2242 dv.view->show_region_editor ();
2249 _editor->begin_reversible_command(_("Ripple drag"));
2251 // remove the regions being rippled from the dragging view, updating them to
2252 // their new positions
2253 remove_unselected_from_views (prev_amount, true);
2255 if (allow_moves_across_tracks) {
2257 // if regions were dragged across tracks, we've rippled any later
2258 // regions on the track the regions were dragged off, so we need
2259 // to add the original track to the undo record
2260 orig_tav->playlist()->clear_changes();
2261 vector<Command*> cmds;
2262 orig_tav->playlist()->rdiff (cmds);
2263 _editor->session()->add_commands (cmds);
2265 if (prev_tav && prev_tav != orig_tav) {
2266 prev_tav->playlist()->clear_changes();
2267 vector<Command*> cmds;
2268 prev_tav->playlist()->rdiff (cmds);
2269 _editor->session()->add_commands (cmds);
2272 // selection spanned multiple tracks - all will need adding to undo record
2274 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2275 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2277 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2278 (*pi)->clear_changes();
2279 vector<Command*> cmds;
2280 (*pi)->rdiff (cmds);
2281 _editor->session()->add_commands (cmds);
2285 // other modified playlists are added to undo by RegionMoveDrag::finished()
2286 RegionMoveDrag::finished (event, movement_occurred);
2287 _editor->commit_reversible_command();
2291 RegionRippleDrag::aborted (bool movement_occurred)
2293 RegionMoveDrag::aborted (movement_occurred);
2298 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2300 _view (dynamic_cast<MidiTimeAxisView*> (v))
2302 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2308 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2311 _editor->begin_reversible_command (_("create region"));
2312 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2313 _view->playlist()->freeze ();
2316 framepos_t const f = adjusted_current_frame (event);
2317 if (f < grab_frame()) {
2318 _region->set_initial_position (f);
2321 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2322 so that if this region is duplicated, its duplicate starts on
2323 a snap point rather than 1 frame after a snap point. Otherwise things get
2324 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2325 place snapped notes at the start of the region.
2328 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2329 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2335 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2337 if (!movement_occurred) {
2338 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2340 _view->playlist()->thaw ();
2341 _editor->commit_reversible_command();
2346 RegionCreateDrag::aborted (bool)
2349 _view->playlist()->thaw ();
2355 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2360 , _was_selected (false)
2363 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2367 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2369 Gdk::Cursor* cursor;
2370 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2372 float x_fraction = cnote->mouse_x_fraction ();
2374 if (x_fraction > 0.0 && x_fraction < 0.25) {
2375 cursor = _editor->cursors()->left_side_trim;
2378 cursor = _editor->cursors()->right_side_trim;
2382 Drag::start_grab (event, cursor);
2384 region = &cnote->region_view();
2387 temp = region->snap_to_pixel (cnote->x0 (), true);
2388 _snap_delta = temp - cnote->x0 ();
2392 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2397 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2398 if (ms.size() > 1) {
2399 /* has to be relative, may make no sense otherwise */
2403 if (!(_was_selected = cnote->selected())) {
2405 /* tertiary-click means extend selection - we'll do that on button release,
2406 so don't add it here, because otherwise we make it hard to figure
2407 out the "extend-to" range.
2410 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2413 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2416 region->note_selected (cnote, true);
2418 _editor->get_selection().clear_points();
2419 region->unique_select (cnote);
2426 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2428 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2430 _editor->begin_reversible_command (_("resize notes"));
2432 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2433 MidiRegionSelection::iterator next;
2436 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2438 mrv->begin_resizing (at_front);
2444 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2445 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2447 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2451 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2453 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2454 if (_editor->snap_mode () != SnapOff) {
2458 if (_editor->snap_mode () == SnapOff) {
2460 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2461 if (apply_snap_delta) {
2467 if (apply_snap_delta) {
2471 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2477 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2479 if (!movement_occurred) {
2480 /* no motion - select note */
2481 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2482 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2483 _editor->current_mouse_mode() == Editing::MouseDraw) {
2485 bool changed = false;
2487 if (_was_selected) {
2488 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2490 region->note_deselected (cnote);
2493 _editor->get_selection().clear_points();
2494 region->unique_select (cnote);
2498 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2499 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2501 if (!extend && !add && region->selection_size() > 1) {
2502 _editor->get_selection().clear_points();
2503 region->unique_select (cnote);
2505 } else if (extend) {
2506 region->note_selected (cnote, true, true);
2509 /* it was added during button press */
2515 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2516 _editor->commit_reversible_selection_op();
2523 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2524 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2525 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2527 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2530 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2532 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2533 if (_editor->snap_mode () != SnapOff) {
2537 if (_editor->snap_mode () == SnapOff) {
2539 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2540 if (apply_snap_delta) {
2546 if (apply_snap_delta) {
2550 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2554 _editor->commit_reversible_command ();
2558 NoteResizeDrag::aborted (bool)
2560 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2561 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2562 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2564 mrv->abort_resizing ();
2569 AVDraggingView::AVDraggingView (RegionView* v)
2572 initial_position = v->region()->position ();
2575 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2578 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2581 TrackViewList empty;
2583 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2584 std::list<RegionView*> views = rs.by_layer();
2587 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2588 RegionView* rv = (*i);
2589 if (!rv->region()->video_locked()) {
2592 if (rv->region()->locked()) {
2595 _views.push_back (AVDraggingView (rv));
2600 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2602 Drag::start_grab (event);
2603 if (_editor->session() == 0) {
2607 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2613 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2617 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2618 _max_backwards_drag = (
2619 ARDOUR_UI::instance()->video_timeline->get_duration()
2620 + ARDOUR_UI::instance()->video_timeline->get_offset()
2621 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2624 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2625 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2626 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2629 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2632 Timecode::Time timecode;
2633 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2634 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);
2635 show_verbose_cursor_text (buf);
2639 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2641 if (_editor->session() == 0) {
2644 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2648 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2652 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2653 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2655 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2656 dt = - _max_backwards_drag;
2659 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2660 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2662 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2663 RegionView* rv = i->view;
2664 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2667 rv->region()->clear_changes ();
2668 rv->region()->suspend_property_changes();
2670 rv->region()->set_position(i->initial_position + dt);
2671 rv->region_changed(ARDOUR::Properties::position);
2674 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2675 Timecode::Time timecode;
2676 Timecode::Time timediff;
2678 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2679 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2680 snprintf (buf, sizeof (buf),
2681 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2682 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2683 , _("Video Start:"),
2684 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2686 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2688 show_verbose_cursor_text (buf);
2692 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2694 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2701 if (!movement_occurred || ! _editor->session()) {
2705 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2707 _editor->begin_reversible_command (_("Move Video"));
2709 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2710 ARDOUR_UI::instance()->video_timeline->save_undo();
2711 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2712 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2714 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2715 i->view->drag_end();
2716 i->view->region()->resume_property_changes ();
2718 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2721 _editor->session()->maybe_update_session_range(
2722 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2723 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2727 _editor->commit_reversible_command ();
2731 VideoTimeLineDrag::aborted (bool)
2733 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2736 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2737 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2739 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2740 i->view->region()->resume_property_changes ();
2741 i->view->region()->set_position(i->initial_position);
2745 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2746 : RegionDrag (e, i, p, v)
2747 , _operation (StartTrim)
2748 , _preserve_fade_anchor (preserve_fade_anchor)
2749 , _jump_position_when_done (false)
2751 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2755 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2758 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2759 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2761 if (tv && tv->is_track()) {
2762 speed = tv->track()->speed();
2765 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2766 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2767 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2769 framepos_t const pf = adjusted_current_frame (event);
2770 setup_snap_delta (region_start);
2772 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2773 /* Move the contents of the region around without changing the region bounds */
2774 _operation = ContentsTrim;
2775 Drag::start_grab (event, _editor->cursors()->trimmer);
2777 /* These will get overridden for a point trim.*/
2778 if (pf < (region_start + region_length/2)) {
2779 /* closer to front */
2780 _operation = StartTrim;
2781 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2782 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2784 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2788 _operation = EndTrim;
2789 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2790 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2792 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2796 /* jump trim disabled for now
2797 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2798 _jump_position_when_done = true;
2802 switch (_operation) {
2804 show_verbose_cursor_time (region_start);
2807 show_verbose_cursor_duration (region_start, region_end);
2810 show_verbose_cursor_time (pf);
2814 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2815 i->view->region()->suspend_property_changes ();
2820 TrimDrag::motion (GdkEvent* event, bool first_move)
2822 RegionView* rv = _primary;
2825 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2826 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2827 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2828 frameoffset_t frame_delta = 0;
2830 if (tv && tv->is_track()) {
2831 speed = tv->track()->speed();
2833 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2834 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2840 switch (_operation) {
2842 trim_type = "Region start trim";
2845 trim_type = "Region end trim";
2848 trim_type = "Region content trim";
2855 _editor->begin_reversible_command (trim_type);
2857 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2858 RegionView* rv = i->view;
2859 rv->region()->playlist()->clear_owned_changes ();
2861 if (_operation == StartTrim) {
2862 rv->trim_front_starting ();
2865 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2868 arv->temporarily_hide_envelope ();
2872 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2873 insert_result = _editor->motion_frozen_playlists.insert (pl);
2875 if (insert_result.second) {
2881 bool non_overlap_trim = false;
2883 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2884 non_overlap_trim = true;
2887 /* contstrain trim to fade length */
2888 if (_preserve_fade_anchor) {
2889 switch (_operation) {
2891 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2892 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2894 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2895 if (ar->locked()) continue;
2896 framecnt_t len = ar->fade_in()->back()->when;
2897 if (len < dt) dt = min(dt, len);
2901 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2902 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2904 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2905 if (ar->locked()) continue;
2906 framecnt_t len = ar->fade_out()->back()->when;
2907 if (len < -dt) dt = max(dt, -len);
2916 switch (_operation) {
2918 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2919 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2920 , _editor->get_grid_music_divisions (event->button.state));
2922 if (changed && _preserve_fade_anchor) {
2923 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2925 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2926 framecnt_t len = ar->fade_in()->back()->when;
2927 framecnt_t diff = ar->first_frame() - i->initial_position;
2928 framepos_t new_length = len - diff;
2929 i->anchored_fade_length = min (ar->length(), new_length);
2930 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2931 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2938 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2939 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, _editor->get_grid_music_divisions (event->button.state));
2940 if (changed && _preserve_fade_anchor) {
2941 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2943 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2944 framecnt_t len = ar->fade_out()->back()->when;
2945 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2946 framepos_t new_length = len + diff;
2947 i->anchored_fade_length = min (ar->length(), new_length);
2948 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2949 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2957 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2959 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2960 i->view->move_contents (frame_delta);
2966 switch (_operation) {
2968 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2971 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2974 // show_verbose_cursor_time (frame_delta);
2980 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2982 if (movement_occurred) {
2983 motion (event, false);
2985 if (_operation == StartTrim) {
2986 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2988 /* This must happen before the region's StatefulDiffCommand is created, as it may
2989 `correct' (ahem) the region's _start from being negative to being zero. It
2990 needs to be zero in the undo record.
2992 i->view->trim_front_ending ();
2994 if (_preserve_fade_anchor) {
2995 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2997 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2998 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2999 ar->set_fade_in_length(i->anchored_fade_length);
3000 ar->set_fade_in_active(true);
3003 if (_jump_position_when_done) {
3004 i->view->region()->set_position (i->initial_position);
3007 } else if (_operation == EndTrim) {
3008 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3009 if (_preserve_fade_anchor) {
3010 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3012 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3013 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3014 ar->set_fade_out_length(i->anchored_fade_length);
3015 ar->set_fade_out_active(true);
3018 if (_jump_position_when_done) {
3019 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3024 if (!_views.empty()) {
3025 if (_operation == StartTrim) {
3026 _editor->maybe_locate_with_edit_preroll(
3027 _views.begin()->view->region()->position());
3029 if (_operation == EndTrim) {
3030 _editor->maybe_locate_with_edit_preroll(
3031 _views.begin()->view->region()->position() +
3032 _views.begin()->view->region()->length());
3036 if (!_editor->selection->selected (_primary)) {
3037 _primary->thaw_after_trim ();
3040 set<boost::shared_ptr<Playlist> > diffed_playlists;
3042 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3043 i->view->thaw_after_trim ();
3044 i->view->enable_display (true);
3046 /* Trimming one region may affect others on the playlist, so we need
3047 to get undo Commands from the whole playlist rather than just the
3048 region. Use diffed_playlists to make sure we don't diff a given
3049 playlist more than once.
3051 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3052 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3053 vector<Command*> cmds;
3055 _editor->session()->add_commands (cmds);
3056 diffed_playlists.insert (p);
3061 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3065 _editor->motion_frozen_playlists.clear ();
3066 _editor->commit_reversible_command();
3069 /* no mouse movement */
3070 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3071 _editor->point_trim (event, adjusted_current_frame (event));
3075 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3076 i->view->region()->resume_property_changes ();
3081 TrimDrag::aborted (bool movement_occurred)
3083 /* Our motion method is changing model state, so use the Undo system
3084 to cancel. Perhaps not ideal, as this will leave an Undo point
3085 behind which may be slightly odd from the user's point of view.
3090 if (movement_occurred) {
3094 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3095 i->view->region()->resume_property_changes ();
3100 TrimDrag::setup_pointer_frame_offset ()
3102 list<DraggingView>::iterator i = _views.begin ();
3103 while (i != _views.end() && i->view != _primary) {
3107 if (i == _views.end()) {
3111 switch (_operation) {
3113 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3116 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3123 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3126 , _old_snap_type (e->snap_type())
3127 , _old_snap_mode (e->snap_mode())
3130 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3131 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3133 _real_section = &_marker->meter();
3138 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3140 Drag::start_grab (event, cursor);
3141 show_verbose_cursor_time (adjusted_current_frame(event));
3145 MeterMarkerDrag::setup_pointer_frame_offset ()
3147 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3151 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3154 // create a dummy marker to catch events, then hide it.
3157 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3159 _marker = new MeterMarker (
3161 *_editor->meter_group,
3162 UIConfiguration::instance().color ("meter marker"),
3164 *new MeterSection (_marker->meter())
3167 /* use the new marker for the grab */
3168 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3171 TempoMap& map (_editor->session()->tempo_map());
3172 /* get current state */
3173 before_state = &map.get_state();
3176 _editor->begin_reversible_command (_("move meter mark"));
3178 _editor->begin_reversible_command (_("copy meter mark"));
3180 Timecode::BBT_Time bbt = _real_section->bbt();
3182 /* we can't add a meter where one currently exists */
3183 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3188 const double beat = map.beat_at_bbt (bbt);
3189 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3190 , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style());
3191 if (!_real_section) {
3197 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3198 if (_real_section->position_lock_style() != AudioTime) {
3199 _editor->set_snap_to (SnapToBar);
3200 _editor->set_snap_mode (SnapNormal);
3204 framepos_t pf = adjusted_current_frame (event);
3206 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3207 /* never snap to music for audio locked */
3208 pf = adjusted_current_frame (event, false);
3211 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3213 /* fake marker meeds to stay under the mouse, unlike the real one. */
3214 _marker->set_position (adjusted_current_frame (event, false));
3216 show_verbose_cursor_time (_real_section->frame());
3220 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3222 if (!movement_occurred) {
3223 if (was_double_click()) {
3224 _editor->edit_meter_marker (*_marker);
3229 /* reinstate old snap setting */
3230 _editor->set_snap_to (_old_snap_type);
3231 _editor->set_snap_mode (_old_snap_mode);
3233 TempoMap& map (_editor->session()->tempo_map());
3235 XMLNode &after = map.get_state();
3236 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3237 _editor->commit_reversible_command ();
3239 // delete the dummy marker we used for visual representation while moving.
3240 // a new visual marker will show up automatically.
3245 MeterMarkerDrag::aborted (bool moved)
3247 _marker->set_position (_marker->meter().frame ());
3249 /* reinstate old snap setting */
3250 _editor->set_snap_to (_old_snap_type);
3251 _editor->set_snap_mode (_old_snap_mode);
3253 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3254 // delete the dummy marker we used for visual representation while moving.
3255 // a new visual marker will show up automatically.
3260 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3265 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3267 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3268 _real_section = &_marker->tempo();
3269 _movable = _real_section->movable();
3274 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3276 Drag::start_grab (event, cursor);
3277 if (!_real_section->active()) {
3278 show_verbose_cursor_text (_("inactive"));
3280 show_verbose_cursor_time (adjusted_current_frame (event));
3285 TempoMarkerDrag::setup_pointer_frame_offset ()
3287 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3291 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3293 if (!_real_section->active()) {
3299 // mvc drag - create a dummy marker to catch events, hide it.
3302 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3304 TempoSection section (_marker->tempo());
3306 _marker = new TempoMarker (
3308 *_editor->tempo_group,
3309 UIConfiguration::instance().color ("tempo marker"),
3311 *new TempoSection (_marker->tempo())
3314 /* use the new marker for the grab */
3315 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3318 TempoMap& map (_editor->session()->tempo_map());
3319 /* get current state */
3320 before_state = &map.get_state();
3323 _editor->begin_reversible_command (_("move tempo mark"));
3326 const Tempo tempo (_marker->tempo());
3327 const framepos_t frame = adjusted_current_frame (event) + 1;
3328 const TempoSection::Type type = _real_section->type();
3330 _editor->begin_reversible_command (_("copy tempo mark"));
3332 if (_real_section->position_lock_style() == MusicTime) {
3333 _real_section = map.add_tempo (tempo, map.pulse_at_frame (frame), 0, type, MusicTime);
3335 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3338 if (!_real_section) {
3346 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3347 /* use vertical movement to alter tempo .. should be log */
3348 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3351 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3353 show_verbose_cursor_text (strs.str());
3355 } else if (_movable && !_real_section->locked_to_meter()) {
3356 const framepos_t pf = adjusted_current_frame (event);
3357 TempoMap& map (_editor->session()->tempo_map());
3359 /* snap to beat is 1, snap to bar is -1 (sorry) */
3360 int sub_num = _editor->get_grid_music_divisions (event->button.state);
3362 map.gui_move_tempo (_real_section, pf, sub_num);
3364 show_verbose_cursor_time (_real_section->frame());
3366 _marker->set_position (adjusted_current_frame (event, false));
3370 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3372 if (!_real_section->active()) {
3375 if (!movement_occurred) {
3376 if (was_double_click()) {
3377 _editor->edit_tempo_marker (*_marker);
3382 TempoMap& map (_editor->session()->tempo_map());
3384 XMLNode &after = map.get_state();
3385 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3386 _editor->commit_reversible_command ();
3388 // delete the dummy marker we used for visual representation while moving.
3389 // a new visual marker will show up automatically.
3394 TempoMarkerDrag::aborted (bool moved)
3396 _marker->set_position (_marker->tempo().frame());
3398 TempoMap& map (_editor->session()->tempo_map());
3399 map.set_state (*before_state, Stateful::current_state_version);
3400 // delete the dummy (hidden) marker we used for events while moving.
3405 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3411 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3416 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3418 Drag::start_grab (event, cursor);
3419 TempoMap& map (_editor->session()->tempo_map());
3420 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3423 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3424 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3425 show_verbose_cursor_text (sstr.str());
3426 finished (event, false);
3430 BBTRulerDrag::setup_pointer_frame_offset ()
3432 TempoMap& map (_editor->session()->tempo_map());
3433 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3434 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3437 if (divisions > 0) {
3438 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3440 /* while it makes some sense for the user to determine the division to 'grab',
3441 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3442 and the result over steep tempo curves. Use sixteenths.
3444 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3447 _pulse = map.pulse_at_beat (beat);
3449 _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3454 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3456 TempoMap& map (_editor->session()->tempo_map());
3459 /* get current state */
3460 before_state = &map.get_state();
3461 _editor->begin_reversible_command (_("dilate tempo"));
3466 if (_editor->snap_musical()) {
3467 pf = adjusted_current_frame (event, false);
3469 pf = adjusted_current_frame (event);
3472 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3473 /* adjust previous tempo to match pointer frame */
3474 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3477 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3478 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3479 show_verbose_cursor_text (sstr.str());
3483 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3485 if (!movement_occurred) {
3489 TempoMap& map (_editor->session()->tempo_map());
3491 XMLNode &after = map.get_state();
3492 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3493 _editor->commit_reversible_command ();
3497 BBTRulerDrag::aborted (bool moved)
3500 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3505 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3506 : Drag (e, &c.track_canvas_item(), false)
3511 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3514 /** Do all the things we do when dragging the playhead to make it look as though
3515 * we have located, without actually doing the locate (because that would cause
3516 * the diskstream buffers to be refilled, which is too slow).
3519 CursorDrag::fake_locate (framepos_t t)
3521 if (_editor->session () == 0) {
3525 _editor->playhead_cursor->set_position (t);
3527 Session* s = _editor->session ();
3528 if (s->timecode_transmission_suspended ()) {
3529 framepos_t const f = _editor->playhead_cursor->current_frame ();
3530 /* This is asynchronous so it will be sent "now"
3532 s->send_mmc_locate (f);
3533 /* These are synchronous and will be sent during the next
3536 s->queue_full_time_code ();
3537 s->queue_song_position_pointer ();
3540 show_verbose_cursor_time (t);
3541 _editor->UpdateAllTransportClocks (t);
3545 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3547 Drag::start_grab (event, c);
3548 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3550 _grab_zoom = _editor->samples_per_pixel;
3552 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3554 _editor->snap_to_with_modifier (where, event);
3556 _editor->_dragging_playhead = true;
3558 Session* s = _editor->session ();
3560 /* grab the track canvas item as well */
3562 _cursor.track_canvas_item().grab();
3565 if (_was_rolling && _stop) {
3569 if (s->is_auditioning()) {
3570 s->cancel_audition ();
3574 if (AudioEngine::instance()->connected()) {
3576 /* do this only if we're the engine is connected
3577 * because otherwise this request will never be
3578 * serviced and we'll busy wait forever. likewise,
3579 * notice if we are disconnected while waiting for the
3580 * request to be serviced.
3583 s->request_suspend_timecode_transmission ();
3584 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3585 /* twiddle our thumbs */
3590 fake_locate (where - snap_delta (event->button.state));
3594 CursorDrag::motion (GdkEvent* event, bool)
3596 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3597 _editor->snap_to_with_modifier (where, event);
3598 if (where != last_pointer_frame()) {
3599 fake_locate (where - snap_delta (event->button.state));
3604 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3606 _editor->_dragging_playhead = false;
3608 _cursor.track_canvas_item().ungrab();
3610 if (!movement_occurred && _stop) {
3614 motion (event, false);
3616 Session* s = _editor->session ();
3618 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3619 _editor->_pending_locate_request = true;
3620 s->request_resume_timecode_transmission ();
3625 CursorDrag::aborted (bool)
3627 _cursor.track_canvas_item().ungrab();
3629 if (_editor->_dragging_playhead) {
3630 _editor->session()->request_resume_timecode_transmission ();
3631 _editor->_dragging_playhead = false;
3634 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3637 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3638 : RegionDrag (e, i, p, v)
3640 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3644 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3646 Drag::start_grab (event, cursor);
3648 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3649 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3650 setup_snap_delta (r->position ());
3652 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3656 FadeInDrag::setup_pointer_frame_offset ()
3658 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3659 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3660 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3664 FadeInDrag::motion (GdkEvent* event, bool)
3666 framecnt_t fade_length;
3668 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3669 _editor->snap_to_with_modifier (pos, event);
3670 pos -= snap_delta (event->button.state);
3672 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3674 if (pos < (region->position() + 64)) {
3675 fade_length = 64; // this should be a minimum defined somewhere
3676 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3677 fade_length = region->length() - region->fade_out()->back()->when - 1;
3679 fade_length = pos - region->position();
3682 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3684 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3690 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3693 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3697 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3699 if (!movement_occurred) {
3703 framecnt_t fade_length;
3704 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3705 _editor->snap_to_with_modifier (pos, event);
3706 pos -= snap_delta (event->button.state);
3708 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3710 if (pos < (region->position() + 64)) {
3711 fade_length = 64; // this should be a minimum defined somewhere
3712 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3713 fade_length = region->length() - region->fade_out()->back()->when - 1;
3715 fade_length = pos - region->position();
3718 bool in_command = false;
3720 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3722 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3728 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3729 XMLNode &before = alist->get_state();
3731 tmp->audio_region()->set_fade_in_length (fade_length);
3732 tmp->audio_region()->set_fade_in_active (true);
3735 _editor->begin_reversible_command (_("change fade in length"));
3738 XMLNode &after = alist->get_state();
3739 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3743 _editor->commit_reversible_command ();
3748 FadeInDrag::aborted (bool)
3750 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3751 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3757 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3761 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3762 : RegionDrag (e, i, p, v)
3764 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3768 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3770 Drag::start_grab (event, cursor);
3772 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3773 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3774 setup_snap_delta (r->last_frame ());
3776 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3780 FadeOutDrag::setup_pointer_frame_offset ()
3782 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3783 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3784 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3788 FadeOutDrag::motion (GdkEvent* event, bool)
3790 framecnt_t fade_length;
3792 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3793 _editor->snap_to_with_modifier (pos, event);
3794 pos -= snap_delta (event->button.state);
3796 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3798 if (pos > (region->last_frame() - 64)) {
3799 fade_length = 64; // this should really be a minimum fade defined somewhere
3800 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3801 fade_length = region->length() - region->fade_in()->back()->when - 1;
3803 fade_length = region->last_frame() - pos;
3806 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3808 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3814 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3817 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3821 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3823 if (!movement_occurred) {
3827 framecnt_t fade_length;
3829 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3830 _editor->snap_to_with_modifier (pos, event);
3831 pos -= snap_delta (event->button.state);
3833 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3835 if (pos > (region->last_frame() - 64)) {
3836 fade_length = 64; // this should really be a minimum fade defined somewhere
3837 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3838 fade_length = region->length() - region->fade_in()->back()->when - 1;
3840 fade_length = region->last_frame() - pos;
3843 bool in_command = false;
3845 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3847 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3853 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3854 XMLNode &before = alist->get_state();
3856 tmp->audio_region()->set_fade_out_length (fade_length);
3857 tmp->audio_region()->set_fade_out_active (true);
3860 _editor->begin_reversible_command (_("change fade out length"));
3863 XMLNode &after = alist->get_state();
3864 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3868 _editor->commit_reversible_command ();
3873 FadeOutDrag::aborted (bool)
3875 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3876 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3882 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3886 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3888 , _selection_changed (false)
3890 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3891 Gtk::Window* toplevel = _editor->current_toplevel();
3892 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3896 _points.push_back (ArdourCanvas::Duple (0, 0));
3898 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3901 MarkerDrag::~MarkerDrag ()
3903 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3908 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3910 location = new Location (*l);
3911 markers.push_back (m);
3916 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3918 Drag::start_grab (event, cursor);
3922 Location *location = _editor->find_location_from_marker (_marker, is_start);
3923 _editor->_dragging_edit_point = true;
3925 update_item (location);
3927 // _drag_line->show();
3928 // _line->raise_to_top();
3931 show_verbose_cursor_time (location->start());
3933 show_verbose_cursor_time (location->end());
3935 setup_snap_delta (is_start ? location->start() : location->end());
3937 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3940 case Selection::Toggle:
3941 /* we toggle on the button release */
3943 case Selection::Set:
3944 if (!_editor->selection->selected (_marker)) {
3945 _editor->selection->set (_marker);
3946 _selection_changed = true;
3949 case Selection::Extend:
3951 Locations::LocationList ll;
3952 list<ArdourMarker*> to_add;
3954 _editor->selection->markers.range (s, e);
3955 s = min (_marker->position(), s);
3956 e = max (_marker->position(), e);
3959 if (e < max_framepos) {
3962 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3963 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3964 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3967 to_add.push_back (lm->start);
3970 to_add.push_back (lm->end);
3974 if (!to_add.empty()) {
3975 _editor->selection->add (to_add);
3976 _selection_changed = true;
3980 case Selection::Add:
3981 _editor->selection->add (_marker);
3982 _selection_changed = true;
3987 /* Set up copies for us to manipulate during the drag
3990 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3992 Location* l = _editor->find_location_from_marker (*i, is_start);
3999 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4001 /* range: check that the other end of the range isn't
4004 CopiedLocationInfo::iterator x;
4005 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4006 if (*(*x).location == *l) {
4010 if (x == _copied_locations.end()) {
4011 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4013 (*x).markers.push_back (*i);
4014 (*x).move_both = true;
4022 MarkerDrag::setup_pointer_frame_offset ()
4025 Location *location = _editor->find_location_from_marker (_marker, is_start);
4026 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4030 MarkerDrag::motion (GdkEvent* event, bool)
4032 framecnt_t f_delta = 0;
4034 bool move_both = false;
4035 Location *real_location;
4036 Location *copy_location = 0;
4037 framecnt_t const sd = snap_delta (event->button.state);
4039 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4040 framepos_t next = newframe;
4042 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4046 CopiedLocationInfo::iterator x;
4048 /* find the marker we're dragging, and compute the delta */
4050 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4052 copy_location = (*x).location;
4054 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4056 /* this marker is represented by this
4057 * CopiedLocationMarkerInfo
4060 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4065 if (real_location->is_mark()) {
4066 f_delta = newframe - copy_location->start();
4070 switch (_marker->type()) {
4071 case ArdourMarker::SessionStart:
4072 case ArdourMarker::RangeStart:
4073 case ArdourMarker::LoopStart:
4074 case ArdourMarker::PunchIn:
4075 f_delta = newframe - copy_location->start();
4078 case ArdourMarker::SessionEnd:
4079 case ArdourMarker::RangeEnd:
4080 case ArdourMarker::LoopEnd:
4081 case ArdourMarker::PunchOut:
4082 f_delta = newframe - copy_location->end();
4085 /* what kind of marker is this ? */
4094 if (x == _copied_locations.end()) {
4095 /* hmm, impossible - we didn't find the dragged marker */
4099 /* now move them all */
4101 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4103 copy_location = x->location;
4105 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4109 if (real_location->locked()) {
4113 if (copy_location->is_mark()) {
4117 copy_location->set_start (copy_location->start() + f_delta);
4121 framepos_t new_start = copy_location->start() + f_delta;
4122 framepos_t new_end = copy_location->end() + f_delta;
4124 if (is_start) { // start-of-range marker
4126 if (move_both || (*x).move_both) {
4127 copy_location->set_start (new_start);
4128 copy_location->set_end (new_end);
4129 } else if (new_start < copy_location->end()) {
4130 copy_location->set_start (new_start);
4131 } else if (newframe > 0) {
4132 //_editor->snap_to (next, RoundUpAlways, true);
4133 copy_location->set_end (next);
4134 copy_location->set_start (newframe);
4137 } else { // end marker
4139 if (move_both || (*x).move_both) {
4140 copy_location->set_end (new_end);
4141 copy_location->set_start (new_start);
4142 } else if (new_end > copy_location->start()) {
4143 copy_location->set_end (new_end);
4144 } else if (newframe > 0) {
4145 //_editor->snap_to (next, RoundDownAlways, true);
4146 copy_location->set_start (next);
4147 copy_location->set_end (newframe);
4152 update_item (copy_location);
4154 /* now lookup the actual GUI items used to display this
4155 * location and move them to wherever the copy of the location
4156 * is now. This means that the logic in ARDOUR::Location is
4157 * still enforced, even though we are not (yet) modifying
4158 * the real Location itself.
4161 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4164 lm->set_position (copy_location->start(), copy_location->end());
4169 assert (!_copied_locations.empty());
4171 show_verbose_cursor_time (newframe);
4175 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4177 if (!movement_occurred) {
4179 if (was_double_click()) {
4180 _editor->rename_marker (_marker);
4184 /* just a click, do nothing but finish
4185 off the selection process
4188 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4190 case Selection::Set:
4191 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4192 _editor->selection->set (_marker);
4193 _selection_changed = true;
4197 case Selection::Toggle:
4198 /* we toggle on the button release, click only */
4199 _editor->selection->toggle (_marker);
4200 _selection_changed = true;
4204 case Selection::Extend:
4205 case Selection::Add:
4209 if (_selection_changed) {
4210 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4211 _editor->commit_reversible_selection_op();
4217 _editor->_dragging_edit_point = false;
4219 XMLNode &before = _editor->session()->locations()->get_state();
4220 bool in_command = false;
4222 MarkerSelection::iterator i;
4223 CopiedLocationInfo::iterator x;
4226 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4227 x != _copied_locations.end() && i != _editor->selection->markers.end();
4230 Location * location = _editor->find_location_from_marker (*i, is_start);
4234 if (location->locked()) {
4238 _editor->begin_reversible_command ( _("move marker") );
4241 if (location->is_mark()) {
4242 location->set_start (((*x).location)->start());
4244 location->set (((*x).location)->start(), ((*x).location)->end());
4247 if (location->is_session_range()) {
4248 _editor->session()->set_end_is_free (false);
4254 XMLNode &after = _editor->session()->locations()->get_state();
4255 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4256 _editor->commit_reversible_command ();
4261 MarkerDrag::aborted (bool movement_occurred)
4263 if (!movement_occurred) {
4267 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4269 /* move all markers to their original location */
4272 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4275 Location * location = _editor->find_location_from_marker (*m, is_start);
4278 (*m)->set_position (is_start ? location->start() : location->end());
4285 MarkerDrag::update_item (Location*)
4290 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4292 , _fixed_grab_x (0.0)
4293 , _fixed_grab_y (0.0)
4294 , _cumulative_x_drag (0.0)
4295 , _cumulative_y_drag (0.0)
4299 if (_zero_gain_fraction < 0.0) {
4300 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4303 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4305 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4311 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4313 Drag::start_grab (event, _editor->cursors()->fader);
4315 // start the grab at the center of the control point so
4316 // the point doesn't 'jump' to the mouse after the first drag
4317 _fixed_grab_x = _point->get_x();
4318 _fixed_grab_y = _point->get_y();
4320 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4321 setup_snap_delta (pos);
4323 float const fraction = 1 - (_point->get_y() / _point->line().height());
4324 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4326 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4328 if (!_point->can_slide ()) {
4329 _x_constrained = true;
4334 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4336 double dx = _drags->current_pointer_x() - last_pointer_x();
4337 double dy = current_pointer_y() - last_pointer_y();
4338 bool need_snap = true;
4340 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4346 /* coordinate in pixels relative to the start of the region (for region-based automation)
4347 or track (for track-based automation) */
4348 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4349 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4351 // calculate zero crossing point. back off by .01 to stay on the
4352 // positive side of zero
4353 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4355 if (_x_constrained) {
4358 if (_y_constrained) {
4362 _cumulative_x_drag = cx - _fixed_grab_x;
4363 _cumulative_y_drag = cy - _fixed_grab_y;
4367 cy = min ((double) _point->line().height(), cy);
4369 // make sure we hit zero when passing through
4370 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4374 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4376 if (!_x_constrained && need_snap) {
4377 _editor->snap_to_with_modifier (cx_frames, event);
4380 cx_frames -= snap_delta (event->button.state);
4381 cx_frames = min (cx_frames, _point->line().maximum_time());
4383 float const fraction = 1.0 - (cy / _point->line().height());
4386 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4387 _editor->begin_reversible_command (_("automation event move"));
4388 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4390 pair<double, float> result;
4391 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4393 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4397 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4399 if (!movement_occurred) {
4402 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4403 _editor->reset_point_selection ();
4407 _point->line().end_drag (_pushing, _final_index);
4408 _editor->commit_reversible_command ();
4413 ControlPointDrag::aborted (bool)
4415 _point->line().reset ();
4419 ControlPointDrag::active (Editing::MouseMode m)
4421 if (m == Editing::MouseDraw) {
4422 /* always active in mouse draw */
4426 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4427 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4430 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4433 , _fixed_grab_x (0.0)
4434 , _fixed_grab_y (0.0)
4435 , _cumulative_y_drag (0)
4439 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4443 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4445 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4448 _item = &_line->grab_item ();
4450 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4451 origin, and ditto for y.
4454 double mx = event->button.x;
4455 double my = event->button.y;
4457 _line->grab_item().canvas_to_item (mx, my);
4459 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4461 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4462 /* no adjacent points */
4466 Drag::start_grab (event, _editor->cursors()->fader);
4468 /* store grab start in item frame */
4469 double const bx = _line->nth (_before)->get_x();
4470 double const ax = _line->nth (_after)->get_x();
4471 double const click_ratio = (ax - mx) / (ax - bx);
4473 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4478 double fraction = 1.0 - (cy / _line->height());
4480 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4484 LineDrag::motion (GdkEvent* event, bool first_move)
4486 double dy = current_pointer_y() - last_pointer_y();
4488 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4492 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4494 _cumulative_y_drag = cy - _fixed_grab_y;
4497 cy = min ((double) _line->height(), cy);
4499 double const fraction = 1.0 - (cy / _line->height());
4503 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4505 _editor->begin_reversible_command (_("automation range move"));
4506 _line->start_drag_line (_before, _after, initial_fraction);
4509 /* we are ignoring x position for this drag, so we can just pass in anything */
4510 pair<double, float> result;
4512 result = _line->drag_motion (0, fraction, true, false, ignored);
4513 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4517 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4519 if (movement_occurred) {
4520 motion (event, false);
4521 _line->end_drag (false, 0);
4522 _editor->commit_reversible_command ();
4524 /* add a new control point on the line */
4526 AutomationTimeAxisView* atv;
4528 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4529 framepos_t where = grab_frame ();
4532 double cy = _fixed_grab_y;
4534 _line->grab_item().item_to_canvas (cx, cy);
4536 atv->add_automation_event (event, where, cy, false);
4537 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4538 AudioRegionView* arv;
4540 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4541 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4548 LineDrag::aborted (bool)
4553 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4557 _region_view_grab_x (0.0),
4558 _cumulative_x_drag (0),
4562 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4566 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4568 Drag::start_grab (event);
4570 _line = reinterpret_cast<Line*> (_item);
4573 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4575 double cx = event->button.x;
4576 double cy = event->button.y;
4578 _item->parent()->canvas_to_item (cx, cy);
4580 /* store grab start in parent frame */
4581 _region_view_grab_x = cx;
4583 _before = *(float*) _item->get_data ("position");
4585 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4587 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4591 FeatureLineDrag::motion (GdkEvent*, bool)
4593 double dx = _drags->current_pointer_x() - last_pointer_x();
4595 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4597 _cumulative_x_drag += dx;
4599 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4608 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4610 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4612 float *pos = new float;
4615 _line->set_data ("position", pos);
4621 FeatureLineDrag::finished (GdkEvent*, bool)
4623 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4624 _arv->update_transient(_before, _before);
4628 FeatureLineDrag::aborted (bool)
4633 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4635 , _vertical_only (false)
4637 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4641 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4643 Drag::start_grab (event);
4644 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4648 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4655 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4657 framepos_t grab = grab_frame ();
4658 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4659 _editor->snap_to_with_modifier (grab, event);
4661 grab = raw_grab_frame ();
4664 /* base start and end on initial click position */
4674 if (current_pointer_y() < grab_y()) {
4675 y1 = current_pointer_y();
4678 y2 = current_pointer_y();
4682 if (start != end || y1 != y2) {
4684 double x1 = _editor->sample_to_pixel (start);
4685 double x2 = _editor->sample_to_pixel (end);
4686 const double min_dimension = 2.0;
4688 if (_vertical_only) {
4689 /* fixed 10 pixel width */
4693 x2 = min (x1 - min_dimension, x2);
4695 x2 = max (x1 + min_dimension, x2);
4700 y2 = min (y1 - min_dimension, y2);
4702 y2 = max (y1 + min_dimension, y2);
4705 /* translate rect into item space and set */
4707 ArdourCanvas::Rect r (x1, y1, x2, y2);
4709 /* this drag is a _trackview_only == true drag, so the y1 and
4710 * y2 (computed using current_pointer_y() and grab_y()) will be
4711 * relative to the top of the trackview group). The
4712 * rubberband rect has the same parent/scroll offset as the
4713 * the trackview group, so we can use the "r" rect directly
4714 * to set the shape of the rubberband.
4717 _editor->rubberband_rect->set (r);
4718 _editor->rubberband_rect->show();
4719 _editor->rubberband_rect->raise_to_top();
4721 show_verbose_cursor_time (pf);
4723 do_select_things (event, true);
4728 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4732 framepos_t grab = grab_frame ();
4733 framepos_t lpf = last_pointer_frame ();
4735 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4736 grab = raw_grab_frame ();
4737 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4751 if (current_pointer_y() < grab_y()) {
4752 y1 = current_pointer_y();
4755 y2 = current_pointer_y();
4759 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4763 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4765 if (movement_occurred) {
4767 motion (event, false);
4768 do_select_things (event, false);
4774 bool do_deselect = true;
4775 MidiTimeAxisView* mtv;
4777 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4779 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4780 /* nothing selected */
4781 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4782 do_deselect = false;
4786 /* do not deselect if Primary or Tertiary (toggle-select or
4787 * extend-select are pressed.
4790 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4791 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4798 _editor->rubberband_rect->hide();
4802 RubberbandSelectDrag::aborted (bool)
4804 _editor->rubberband_rect->hide ();
4807 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4808 : RegionDrag (e, i, p, v)
4810 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4814 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4816 Drag::start_grab (event, cursor);
4818 _editor->get_selection().add (_primary);
4820 framepos_t where = _primary->region()->position();
4821 setup_snap_delta (where);
4823 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4827 TimeFXDrag::motion (GdkEvent* event, bool)
4829 RegionView* rv = _primary;
4830 StreamView* cv = rv->get_time_axis_view().view ();
4832 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4833 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4834 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4835 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4836 _editor->snap_to_with_modifier (pf, event);
4837 pf -= snap_delta (event->button.state);
4839 if (pf > rv->region()->position()) {
4840 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4843 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4847 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4849 /* this may have been a single click, no drag. We still want the dialog
4850 to show up in that case, so that the user can manually edit the
4851 parameters for the timestretch.
4854 float fraction = 1.0;
4856 if (movement_occurred) {
4858 motion (event, false);
4860 _primary->get_time_axis_view().hide_timestretch ();
4862 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4864 if (adjusted_frame_pos < _primary->region()->position()) {
4865 /* backwards drag of the left edge - not usable */
4869 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4871 fraction = (double) newlen / (double) _primary->region()->length();
4873 #ifndef USE_RUBBERBAND
4874 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4875 if (_primary->region()->data_type() == DataType::AUDIO) {
4876 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4881 if (!_editor->get_selection().regions.empty()) {
4882 /* primary will already be included in the selection, and edit
4883 group shared editing will propagate selection across
4884 equivalent regions, so just use the current region
4888 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4889 error << _("An error occurred while executing time stretch operation") << endmsg;
4895 TimeFXDrag::aborted (bool)
4897 _primary->get_time_axis_view().hide_timestretch ();
4900 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4903 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4907 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4909 Drag::start_grab (event);
4913 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4915 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4919 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4921 if (movement_occurred && _editor->session()) {
4922 /* make sure we stop */
4923 _editor->session()->request_transport_speed (0.0);
4928 ScrubDrag::aborted (bool)
4933 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4937 , _time_selection_at_start (!_editor->get_selection().time.empty())
4939 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4941 if (_time_selection_at_start) {
4942 start_at_start = _editor->get_selection().time.start();
4943 end_at_start = _editor->get_selection().time.end_frame();
4948 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4950 if (_editor->session() == 0) {
4954 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4956 switch (_operation) {
4957 case CreateSelection:
4958 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4963 cursor = _editor->cursors()->selector;
4964 Drag::start_grab (event, cursor);
4967 case SelectionStartTrim:
4968 if (_editor->clicked_axisview) {
4969 _editor->clicked_axisview->order_selection_trims (_item, true);
4971 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4974 case SelectionEndTrim:
4975 if (_editor->clicked_axisview) {
4976 _editor->clicked_axisview->order_selection_trims (_item, false);
4978 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4982 Drag::start_grab (event, cursor);
4985 case SelectionExtend:
4986 Drag::start_grab (event, cursor);
4990 if (_operation == SelectionMove) {
4991 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4993 show_verbose_cursor_time (adjusted_current_frame (event));
4998 SelectionDrag::setup_pointer_frame_offset ()
5000 switch (_operation) {
5001 case CreateSelection:
5002 _pointer_frame_offset = 0;
5005 case SelectionStartTrim:
5007 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5010 case SelectionEndTrim:
5011 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5014 case SelectionExtend:
5020 SelectionDrag::motion (GdkEvent* event, bool first_move)
5022 framepos_t start = 0;
5024 framecnt_t length = 0;
5025 framecnt_t distance = 0;
5027 framepos_t const pending_position = adjusted_current_frame (event);
5029 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5033 switch (_operation) {
5034 case CreateSelection:
5036 framepos_t grab = grab_frame ();
5039 grab = adjusted_current_frame (event, false);
5040 if (grab < pending_position) {
5041 _editor->snap_to (grab, RoundDownMaybe);
5043 _editor->snap_to (grab, RoundUpMaybe);
5047 if (pending_position < grab) {
5048 start = pending_position;
5051 end = pending_position;
5055 /* first drag: Either add to the selection
5056 or create a new selection
5063 /* adding to the selection */
5064 _editor->set_selected_track_as_side_effect (Selection::Add);
5065 _editor->clicked_selection = _editor->selection->add (start, end);
5072 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5073 _editor->set_selected_track_as_side_effect (Selection::Set);
5076 _editor->clicked_selection = _editor->selection->set (start, end);
5080 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5081 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5082 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5084 _editor->selection->add (atest);
5088 /* select all tracks within the rectangle that we've marked out so far */
5089 TrackViewList new_selection;
5090 TrackViewList& all_tracks (_editor->track_views);
5092 ArdourCanvas::Coord const top = grab_y();
5093 ArdourCanvas::Coord const bottom = current_pointer_y();
5095 if (top >= 0 && bottom >= 0) {
5097 //first, find the tracks that are covered in the y range selection
5098 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5099 if ((*i)->covered_by_y_range (top, bottom)) {
5100 new_selection.push_back (*i);
5104 //now find any tracks that are GROUPED with the tracks we selected
5105 TrackViewList grouped_add = new_selection;
5106 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5107 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5108 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5109 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5110 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5111 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5112 grouped_add.push_back (*j);
5117 //now compare our list with the current selection, and add or remove as necessary
5118 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5119 TrackViewList tracks_to_add;
5120 TrackViewList tracks_to_remove;
5121 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5122 if ( !_editor->selection->tracks.contains ( *i ) )
5123 tracks_to_add.push_back ( *i );
5124 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5125 if ( !grouped_add.contains ( *i ) )
5126 tracks_to_remove.push_back ( *i );
5127 _editor->selection->add(tracks_to_add);
5128 _editor->selection->remove(tracks_to_remove);
5134 case SelectionStartTrim:
5136 end = _editor->selection->time[_editor->clicked_selection].end;
5138 if (pending_position > end) {
5141 start = pending_position;
5145 case SelectionEndTrim:
5147 start = _editor->selection->time[_editor->clicked_selection].start;
5149 if (pending_position < start) {
5152 end = pending_position;
5159 start = _editor->selection->time[_editor->clicked_selection].start;
5160 end = _editor->selection->time[_editor->clicked_selection].end;
5162 length = end - start;
5163 distance = pending_position - start;
5164 start = pending_position;
5165 _editor->snap_to (start);
5167 end = start + length;
5171 case SelectionExtend:
5176 switch (_operation) {
5178 if (_time_selection_at_start) {
5179 _editor->selection->move_time (distance);
5183 _editor->selection->replace (_editor->clicked_selection, start, end);
5187 if (_operation == SelectionMove) {
5188 show_verbose_cursor_time(start);
5190 show_verbose_cursor_time(pending_position);
5195 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5197 Session* s = _editor->session();
5199 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5200 if (movement_occurred) {
5201 motion (event, false);
5202 /* XXX this is not object-oriented programming at all. ick */
5203 if (_editor->selection->time.consolidate()) {
5204 _editor->selection->TimeChanged ();
5207 /* XXX what if its a music time selection? */
5209 if (s->get_play_range() && s->transport_rolling()) {
5210 s->request_play_range (&_editor->selection->time, true);
5211 } else if (!s->config.get_external_sync()) {
5212 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5213 if (_operation == SelectionEndTrim)
5214 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5216 s->request_locate (_editor->get_selection().time.start());
5220 if (_editor->get_selection().time.length() != 0) {
5221 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5223 s->clear_range_selection ();
5228 /* just a click, no pointer movement.
5231 if (_operation == SelectionExtend) {
5232 if (_time_selection_at_start) {
5233 framepos_t pos = adjusted_current_frame (event, false);
5234 framepos_t start = min (pos, start_at_start);
5235 framepos_t end = max (pos, end_at_start);
5236 _editor->selection->set (start, end);
5239 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5240 if (_editor->clicked_selection) {
5241 _editor->selection->remove (_editor->clicked_selection);
5244 if (!_editor->clicked_selection) {
5245 _editor->selection->clear_time();
5250 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5251 _editor->selection->set (_editor->clicked_axisview);
5254 if (s && s->get_play_range () && s->transport_rolling()) {
5255 s->request_stop (false, false);
5260 _editor->stop_canvas_autoscroll ();
5261 _editor->clicked_selection = 0;
5262 _editor->commit_reversible_selection_op ();
5266 SelectionDrag::aborted (bool)
5271 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5272 : Drag (e, i, false),
5276 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5278 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5279 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5280 physical_screen_height (_editor->current_toplevel()->get_window())));
5281 _drag_rect->hide ();
5283 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5284 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5287 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5289 /* normal canvas items will be cleaned up when their parent group is deleted. But
5290 this item is created as the child of a long-lived parent group, and so we
5291 need to explicitly delete it.
5297 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5299 if (_editor->session() == 0) {
5303 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5305 if (!_editor->temp_location) {
5306 _editor->temp_location = new Location (*_editor->session());
5309 switch (_operation) {
5310 case CreateSkipMarker:
5311 case CreateRangeMarker:
5312 case CreateTransportMarker:
5313 case CreateCDMarker:
5315 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5320 cursor = _editor->cursors()->selector;
5324 Drag::start_grab (event, cursor);
5326 show_verbose_cursor_time (adjusted_current_frame (event));
5330 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5332 framepos_t start = 0;
5334 ArdourCanvas::Rectangle *crect;
5336 switch (_operation) {
5337 case CreateSkipMarker:
5338 crect = _editor->range_bar_drag_rect;
5340 case CreateRangeMarker:
5341 crect = _editor->range_bar_drag_rect;
5343 case CreateTransportMarker:
5344 crect = _editor->transport_bar_drag_rect;
5346 case CreateCDMarker:
5347 crect = _editor->cd_marker_bar_drag_rect;
5350 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5355 framepos_t const pf = adjusted_current_frame (event);
5357 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5358 framepos_t grab = grab_frame ();
5359 _editor->snap_to (grab);
5361 if (pf < grab_frame()) {
5369 /* first drag: Either add to the selection
5370 or create a new selection.
5375 _editor->temp_location->set (start, end);
5379 update_item (_editor->temp_location);
5381 //_drag_rect->raise_to_top();
5387 _editor->temp_location->set (start, end);
5389 double x1 = _editor->sample_to_pixel (start);
5390 double x2 = _editor->sample_to_pixel (end);
5394 update_item (_editor->temp_location);
5397 show_verbose_cursor_time (pf);
5402 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5404 Location * newloc = 0;
5408 if (movement_occurred) {
5409 motion (event, false);
5412 switch (_operation) {
5413 case CreateSkipMarker:
5414 case CreateRangeMarker:
5415 case CreateCDMarker:
5417 XMLNode &before = _editor->session()->locations()->get_state();
5418 if (_operation == CreateSkipMarker) {
5419 _editor->begin_reversible_command (_("new skip marker"));
5420 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5421 flags = Location::IsRangeMarker | Location::IsSkip;
5422 _editor->range_bar_drag_rect->hide();
5423 } else if (_operation == CreateCDMarker) {
5424 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5425 _editor->begin_reversible_command (_("new CD marker"));
5426 flags = Location::IsRangeMarker | Location::IsCDMarker;
5427 _editor->cd_marker_bar_drag_rect->hide();
5429 _editor->begin_reversible_command (_("new skip marker"));
5430 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5431 flags = Location::IsRangeMarker;
5432 _editor->range_bar_drag_rect->hide();
5434 newloc = new Location (
5435 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5438 _editor->session()->locations()->add (newloc, true);
5439 XMLNode &after = _editor->session()->locations()->get_state();
5440 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5441 _editor->commit_reversible_command ();
5445 case CreateTransportMarker:
5446 // popup menu to pick loop or punch
5447 _editor->new_transport_marker_context_menu (&event->button, _item);
5453 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5455 if (_operation == CreateTransportMarker) {
5457 /* didn't drag, so just locate */
5459 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5461 } else if (_operation == CreateCDMarker) {
5463 /* didn't drag, but mark is already created so do
5466 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5471 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5473 if (end == max_framepos) {
5474 end = _editor->session()->current_end_frame ();
5477 if (start == max_framepos) {
5478 start = _editor->session()->current_start_frame ();
5481 switch (_editor->mouse_mode) {
5483 /* find the two markers on either side and then make the selection from it */
5484 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5488 /* find the two markers on either side of the click and make the range out of it */
5489 _editor->selection->set (start, end);
5498 _editor->stop_canvas_autoscroll ();
5502 RangeMarkerBarDrag::aborted (bool movement_occurred)
5504 if (movement_occurred) {
5505 _drag_rect->hide ();
5510 RangeMarkerBarDrag::update_item (Location* location)
5512 double const x1 = _editor->sample_to_pixel (location->start());
5513 double const x2 = _editor->sample_to_pixel (location->end());
5515 _drag_rect->set_x0 (x1);
5516 _drag_rect->set_x1 (x2);
5519 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5521 , _cumulative_dx (0)
5522 , _cumulative_dy (0)
5523 , _was_selected (false)
5525 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5527 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5529 _region = &_primary->region_view ();
5530 _note_height = _region->midi_stream_view()->note_height ();
5534 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5536 Drag::start_grab (event);
5537 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5539 if (!(_was_selected = _primary->selected())) {
5541 /* tertiary-click means extend selection - we'll do that on button release,
5542 so don't add it here, because otherwise we make it hard to figure
5543 out the "extend-to" range.
5546 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5549 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5552 _region->note_selected (_primary, true);
5554 _editor->get_selection().clear_points();
5555 _region->unique_select (_primary);
5561 /** @return Current total drag x change in frames */
5563 NoteDrag::total_dx (const guint state) const
5566 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5568 /* primary note time */
5569 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5571 /* new time of the primary note in session frames */
5572 frameoffset_t st = n + dx + snap_delta (state);
5574 framepos_t const rp = _region->region()->position ();
5576 /* prevent the note being dragged earlier than the region's position */
5579 /* possibly snap and return corresponding delta */
5583 if (ArdourKeyboard::indicates_snap (state)) {
5584 if (_editor->snap_mode () != SnapOff) {
5588 if (_editor->snap_mode () == SnapOff) {
5590 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5591 if (ArdourKeyboard::indicates_snap_delta (state)) {
5599 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5600 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5602 ret = st - n - snap_delta (state);
5607 /** @return Current total drag y change in note number */
5609 NoteDrag::total_dy () const
5611 MidiStreamView* msv = _region->midi_stream_view ();
5612 double const y = _region->midi_view()->y_position ();
5613 /* new current note */
5614 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5616 n = max (msv->lowest_note(), n);
5617 n = min (msv->highest_note(), n);
5618 /* and work out delta */
5619 return n - msv->y_to_note (grab_y() - y);
5623 NoteDrag::motion (GdkEvent * event, bool)
5625 /* Total change in x and y since the start of the drag */
5626 frameoffset_t const dx = total_dx (event->button.state);
5627 int8_t const dy = total_dy ();
5629 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5630 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5631 double const tdy = -dy * _note_height - _cumulative_dy;
5634 _cumulative_dx += tdx;
5635 _cumulative_dy += tdy;
5637 int8_t note_delta = total_dy();
5639 _region->move_selection (tdx, tdy, note_delta);
5641 /* the new note value may be the same as the old one, but we
5642 * don't know what that means because the selection may have
5643 * involved more than one note and we might be doing something
5644 * odd with them. so show the note value anyway, always.
5647 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5649 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5654 NoteDrag::finished (GdkEvent* ev, bool moved)
5657 /* no motion - select note */
5659 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5660 _editor->current_mouse_mode() == Editing::MouseDraw) {
5662 bool changed = false;
5664 if (_was_selected) {
5665 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5667 _region->note_deselected (_primary);
5670 _editor->get_selection().clear_points();
5671 _region->unique_select (_primary);
5675 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5676 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5678 if (!extend && !add && _region->selection_size() > 1) {
5679 _editor->get_selection().clear_points();
5680 _region->unique_select (_primary);
5682 } else if (extend) {
5683 _region->note_selected (_primary, true, true);
5686 /* it was added during button press */
5693 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5694 _editor->commit_reversible_selection_op();
5698 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5703 NoteDrag::aborted (bool)
5708 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5709 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5710 : Drag (editor, atv->base_item ())
5712 , _y_origin (atv->y_position())
5713 , _nothing_to_drag (false)
5715 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5716 setup (atv->lines ());
5719 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5720 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5721 : Drag (editor, rv->get_canvas_group ())
5723 , _y_origin (rv->get_time_axis_view().y_position())
5724 , _nothing_to_drag (false)
5727 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5729 list<boost::shared_ptr<AutomationLine> > lines;
5731 AudioRegionView* audio_view;
5732 AutomationRegionView* automation_view;
5733 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5734 lines.push_back (audio_view->get_gain_line ());
5735 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5736 lines.push_back (automation_view->line ());
5739 error << _("Automation range drag created for invalid region type") << endmsg;
5745 /** @param lines AutomationLines to drag.
5746 * @param offset Offset from the session start to the points in the AutomationLines.
5749 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5751 /* find the lines that overlap the ranges being dragged */
5752 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5753 while (i != lines.end ()) {
5754 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5757 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5759 /* check this range against all the AudioRanges that we are using */
5760 list<AudioRange>::const_iterator k = _ranges.begin ();
5761 while (k != _ranges.end()) {
5762 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5768 /* add it to our list if it overlaps at all */
5769 if (k != _ranges.end()) {
5774 _lines.push_back (n);
5780 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5784 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5786 return 1.0 - ((global_y - _y_origin) / line->height());
5790 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5792 const double v = list->eval(x);
5793 return _integral ? rint(v) : v;
5797 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5799 Drag::start_grab (event, cursor);
5801 /* Get line states before we start changing things */
5802 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5803 i->state = &i->line->get_state ();
5804 i->original_fraction = y_fraction (i->line, current_pointer_y());
5807 if (_ranges.empty()) {
5809 /* No selected time ranges: drag all points */
5810 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5811 uint32_t const N = i->line->npoints ();
5812 for (uint32_t j = 0; j < N; ++j) {
5813 i->points.push_back (i->line->nth (j));
5819 if (_nothing_to_drag) {
5825 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5827 if (_nothing_to_drag && !first_move) {
5832 _editor->begin_reversible_command (_("automation range move"));
5834 if (!_ranges.empty()) {
5836 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5838 framecnt_t const half = (i->start + i->end) / 2;
5840 /* find the line that this audio range starts in */
5841 list<Line>::iterator j = _lines.begin();
5842 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5846 if (j != _lines.end()) {
5847 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5849 /* j is the line that this audio range starts in; fade into it;
5850 64 samples length plucked out of thin air.
5853 framepos_t a = i->start + 64;
5858 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5859 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5861 XMLNode &before = the_list->get_state();
5862 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5863 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5865 if (add_p || add_q) {
5866 _editor->session()->add_command (
5867 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5871 /* same thing for the end */
5874 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5878 if (j != _lines.end()) {
5879 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5881 /* j is the line that this audio range starts in; fade out of it;
5882 64 samples length plucked out of thin air.
5885 framepos_t b = i->end - 64;
5890 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5891 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5893 XMLNode &before = the_list->get_state();
5894 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5895 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5897 if (add_p || add_q) {
5898 _editor->session()->add_command (
5899 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5904 _nothing_to_drag = true;
5906 /* Find all the points that should be dragged and put them in the relevant
5907 points lists in the Line structs.
5910 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5912 uint32_t const N = i->line->npoints ();
5913 for (uint32_t j = 0; j < N; ++j) {
5915 /* here's a control point on this line */
5916 ControlPoint* p = i->line->nth (j);
5917 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5919 /* see if it's inside a range */
5920 list<AudioRange>::const_iterator k = _ranges.begin ();
5921 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5925 if (k != _ranges.end()) {
5926 /* dragging this point */
5927 _nothing_to_drag = false;
5928 i->points.push_back (p);
5934 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5935 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5939 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5940 float const f = y_fraction (l->line, current_pointer_y());
5941 /* we are ignoring x position for this drag, so we can just pass in anything */
5942 pair<double, float> result;
5944 result = l->line->drag_motion (0, f, true, false, ignored);
5945 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5950 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5952 if (_nothing_to_drag || !motion_occurred) {
5956 motion (event, false);
5957 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5958 i->line->end_drag (false, 0);
5961 _editor->commit_reversible_command ();
5965 AutomationRangeDrag::aborted (bool)
5967 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5972 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5974 , initial_time_axis_view (itav)
5976 /* note that time_axis_view may be null if the regionview was created
5977 * as part of a copy operation.
5979 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5980 layer = v->region()->layer ();
5981 initial_y = v->get_canvas_group()->position().y;
5982 initial_playlist = v->region()->playlist ();
5983 initial_position = v->region()->position ();
5984 initial_end = v->region()->position () + v->region()->length ();
5987 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5988 : Drag (e, i->canvas_item ())
5991 , _cumulative_dx (0)
5993 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5994 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5999 PatchChangeDrag::motion (GdkEvent* ev, bool)
6001 framepos_t f = adjusted_current_frame (ev);
6002 boost::shared_ptr<Region> r = _region_view->region ();
6003 f = max (f, r->position ());
6004 f = min (f, r->last_frame ());
6006 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6007 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6008 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6009 _cumulative_dx = dxu;
6013 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6015 if (!movement_occurred) {
6016 if (was_double_click()) {
6017 _region_view->edit_patch_change (_patch_change);
6022 boost::shared_ptr<Region> r (_region_view->region ());
6023 framepos_t f = adjusted_current_frame (ev);
6024 f = max (f, r->position ());
6025 f = min (f, r->last_frame ());
6027 _region_view->move_patch_change (
6029 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6034 PatchChangeDrag::aborted (bool)
6036 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6040 PatchChangeDrag::setup_pointer_frame_offset ()
6042 boost::shared_ptr<Region> region = _region_view->region ();
6043 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6046 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6047 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6054 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6056 _region_view->update_drag_selection (
6058 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6062 MidiRubberbandSelectDrag::deselect_things ()
6067 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6068 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6071 _vertical_only = true;
6075 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6077 double const y = _region_view->midi_view()->y_position ();
6079 y1 = max (0.0, y1 - y);
6080 y2 = max (0.0, y2 - y);
6082 _region_view->update_vertical_drag_selection (
6085 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6090 MidiVerticalSelectDrag::deselect_things ()
6095 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6096 : RubberbandSelectDrag (e, i)
6102 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6104 if (drag_in_progress) {
6105 /* We just want to select things at the end of the drag, not during it */
6109 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6111 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6113 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6115 _editor->commit_reversible_selection_op ();
6119 EditorRubberbandSelectDrag::deselect_things ()
6121 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6123 _editor->selection->clear_tracks();
6124 _editor->selection->clear_regions();
6125 _editor->selection->clear_points ();
6126 _editor->selection->clear_lines ();
6127 _editor->selection->clear_midi_notes ();
6129 _editor->commit_reversible_selection_op();
6132 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6137 _note[0] = _note[1] = 0;
6140 NoteCreateDrag::~NoteCreateDrag ()
6146 NoteCreateDrag::grid_frames (framepos_t t) const
6149 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6151 grid_beats = Evoral::Beats(1);
6154 return _region_view->region_beats_to_region_frames (grid_beats);
6158 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6160 Drag::start_grab (event, cursor);
6162 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6164 framepos_t pf = _drags->current_pointer_frame ();
6165 framecnt_t const g = grid_frames (pf);
6167 /* Hack so that we always snap to the note that we are over, instead of snapping
6168 to the next one if we're more than halfway through the one we're over.
6170 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6174 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6175 _note[1] = _note[0];
6177 MidiStreamView* sv = _region_view->midi_stream_view ();
6178 double const x = _editor->sample_to_pixel (_note[0]);
6179 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6181 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6182 _drag_rect->set_outline_all ();
6183 _drag_rect->set_outline_color (0xffffff99);
6184 _drag_rect->set_fill_color (0xffffff66);
6188 NoteCreateDrag::motion (GdkEvent* event, bool)
6190 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6191 double const x0 = _editor->sample_to_pixel (_note[0]);
6192 double const x1 = _editor->sample_to_pixel (_note[1]);
6193 _drag_rect->set_x0 (std::min(x0, x1));
6194 _drag_rect->set_x1 (std::max(x0, x1));
6198 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6200 if (!had_movement) {
6204 framepos_t const start = min (_note[0], _note[1]);
6205 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6207 framecnt_t const g = grid_frames (start);
6208 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6210 if (_editor->snap_mode() == SnapNormal && length < g) {
6214 Evoral::Beats length_beats = max (
6215 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6217 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6221 NoteCreateDrag::y_to_region (double y) const
6224 _region_view->get_canvas_group()->canvas_to_item (x, y);
6229 NoteCreateDrag::aborted (bool)
6234 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6239 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6243 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6245 Drag::start_grab (event, cursor);
6249 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6255 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6258 distance = _drags->current_pointer_x() - grab_x();
6259 len = ar->fade_in()->back()->when;
6261 distance = grab_x() - _drags->current_pointer_x();
6262 len = ar->fade_out()->back()->when;
6265 /* how long should it be ? */
6267 new_length = len + _editor->pixel_to_sample (distance);
6269 /* now check with the region that this is legal */
6271 new_length = ar->verify_xfade_bounds (new_length, start);
6274 arv->reset_fade_in_shape_width (ar, new_length);
6276 arv->reset_fade_out_shape_width (ar, new_length);
6281 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6287 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6290 distance = _drags->current_pointer_x() - grab_x();
6291 len = ar->fade_in()->back()->when;
6293 distance = grab_x() - _drags->current_pointer_x();
6294 len = ar->fade_out()->back()->when;
6297 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6299 _editor->begin_reversible_command ("xfade trim");
6300 ar->playlist()->clear_owned_changes ();
6303 ar->set_fade_in_length (new_length);
6305 ar->set_fade_out_length (new_length);
6308 /* Adjusting the xfade may affect other regions in the playlist, so we need
6309 to get undo Commands from the whole playlist rather than just the
6313 vector<Command*> cmds;
6314 ar->playlist()->rdiff (cmds);
6315 _editor->session()->add_commands (cmds);
6316 _editor->commit_reversible_command ();
6321 CrossfadeEdgeDrag::aborted (bool)
6324 // arv->redraw_start_xfade ();
6326 // arv->redraw_end_xfade ();
6330 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6331 : Drag (e, item, true)
6332 , line (new EditorCursor (*e))
6334 line->set_position (pos);
6338 RegionCutDrag::~RegionCutDrag ()
6344 RegionCutDrag::motion (GdkEvent*, bool)
6346 framepos_t where = _drags->current_pointer_frame();
6347 _editor->snap_to (where);
6349 line->set_position (where);
6353 RegionCutDrag::finished (GdkEvent* event, bool)
6355 _editor->get_track_canvas()->canvas()->re_enter();
6357 framepos_t pos = _drags->current_pointer_frame();
6361 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6367 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state));
6371 RegionCutDrag::aborted (bool)