2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "ui_config.h"
68 #include "verbose_cursor.h"
71 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
82 DragManager::DragManager (Editor* e)
85 , _current_pointer_x (0.0)
86 , _current_pointer_y (0.0)
87 , _current_pointer_frame (0)
88 , _old_follow_playhead (false)
92 DragManager::~DragManager ()
97 /** Call abort for each active drag */
103 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 if (!_drags.empty ()) {
109 _editor->set_follow_playhead (_old_follow_playhead, false);
113 _editor->abort_reversible_command();
119 DragManager::add (Drag* d)
121 d->set_manager (this);
122 _drags.push_back (d);
126 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
128 d->set_manager (this);
129 _drags.push_back (d);
134 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
136 /* Prevent follow playhead during the drag to be nice to the user */
137 _old_follow_playhead = _editor->follow_playhead ();
138 _editor->set_follow_playhead (false);
140 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
142 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
143 (*i)->start_grab (e, c);
147 /** Call end_grab for each active drag.
148 * @return true if any drag reported movement having occurred.
151 DragManager::end_grab (GdkEvent* e)
156 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
157 bool const t = (*i)->end_grab (e);
168 _editor->set_follow_playhead (_old_follow_playhead, false);
174 DragManager::mark_double_click ()
176 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
177 (*i)->set_double_click (true);
182 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
186 /* calling this implies that we expect the event to have canvas
189 * Can we guarantee that this is true?
192 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
194 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
195 bool const t = (*i)->motion_handler (e, from_autoscroll);
196 /* run all handlers; return true if at least one of them
197 returns true (indicating that the event has been handled).
209 DragManager::have_item (ArdourCanvas::Item* i) const
211 list<Drag*>::const_iterator j = _drags.begin ();
212 while (j != _drags.end() && (*j)->item () != i) {
216 return j != _drags.end ();
219 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
223 , _pointer_frame_offset (0)
224 , _x_constrained (false)
225 , _y_constrained (false)
226 , _was_rolling (false)
227 , _trackview_only (trackview_only)
228 , _move_threshold_passed (false)
229 , _starting_point_passed (false)
230 , _initially_vertical (false)
231 , _was_double_click (false)
234 , _last_pointer_x (0.0)
235 , _last_pointer_y (0.0)
236 , _raw_grab_frame (0)
238 , _last_pointer_frame (0)
245 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
251 _cursor_ctx = CursorContext::create (*_editor, cursor);
253 _cursor_ctx->change (cursor);
260 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
263 /* we set up x/y dragging constraints on first move */
265 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::snap_delta (guint state) const
349 if (ArdourKeyboard::indicates_snap_delta (state)) {
357 Drag::current_pointer_x() const
359 return _drags->current_pointer_x ();
363 Drag::current_pointer_y () const
365 if (!_trackview_only) {
366 return _drags->current_pointer_y ();
369 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
373 Drag::setup_snap_delta (framepos_t pos)
375 framepos_t temp = pos;
376 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
377 _snap_delta = temp - pos;
381 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
383 /* check to see if we have moved in any way that matters since the last motion event */
384 if (_move_threshold_passed &&
385 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
386 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
390 pair<framecnt_t, int> const threshold = move_threshold ();
392 bool const old_move_threshold_passed = _move_threshold_passed;
394 if (!_move_threshold_passed) {
396 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
397 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
399 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
402 if (active (_editor->mouse_mode) && _move_threshold_passed) {
404 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
406 if (old_move_threshold_passed != _move_threshold_passed) {
410 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
411 _initially_vertical = true;
413 _initially_vertical = false;
415 /** check constraints for this drag.
416 * Note that the current convention is to use "contains" for
417 * key modifiers during motion and "equals" when initiating a drag.
418 * In this case we haven't moved yet, so "equals" applies here.
420 if (Config->get_edit_mode() != Lock) {
421 if (event->motion.state & Gdk::BUTTON2_MASK) {
422 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
423 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
424 _x_constrained = false;
425 _y_constrained = true;
427 _x_constrained = true;
428 _y_constrained = false;
430 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
431 // if dragging normally, the motion is constrained to the first direction of movement.
432 if (_initially_vertical) {
433 _x_constrained = true;
434 _y_constrained = false;
436 _x_constrained = false;
437 _y_constrained = true;
441 if (event->button.state & Gdk::BUTTON2_MASK) {
442 _x_constrained = false;
444 _x_constrained = true;
446 _y_constrained = false;
450 if (!from_autoscroll) {
451 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
454 if (!_editor->autoscroll_active() || from_autoscroll) {
457 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
459 motion (event, first_move && !_starting_point_passed);
461 if (first_move && !_starting_point_passed) {
462 _starting_point_passed = true;
465 _last_pointer_x = _drags->current_pointer_x ();
466 _last_pointer_y = current_pointer_y ();
467 _last_pointer_frame = adjusted_current_frame (event, false);
477 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
485 aborted (_move_threshold_passed);
487 _editor->stop_canvas_autoscroll ();
488 _editor->verbose_cursor()->hide ();
492 Drag::show_verbose_cursor_time (framepos_t frame)
494 _editor->verbose_cursor()->set_time (frame);
495 _editor->verbose_cursor()->show ();
499 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
501 _editor->verbose_cursor()->set_duration (start, end);
502 _editor->verbose_cursor()->show ();
506 Drag::show_verbose_cursor_text (string const & text)
508 _editor->verbose_cursor()->set (text);
509 _editor->verbose_cursor()->show ();
512 boost::shared_ptr<Region>
513 Drag::add_midi_region (MidiTimeAxisView* view, bool commit, const int32_t sub_num)
515 if (_editor->session()) {
516 const TempoMap& map (_editor->session()->tempo_map());
517 framecnt_t pos = grab_frame();
518 /* not that the frame rate used here can be affected by pull up/down which
521 framecnt_t len = map.frame_at_beat (map.beat_at_frame (pos) + 1.0) - pos;
522 return view->add_region (grab_frame(), len, commit, sub_num);
525 return boost::shared_ptr<Region>();
528 struct PresentationInfoTimeAxisViewSorter {
529 bool operator() (TimeAxisView* a, TimeAxisView* b) {
530 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
534 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
539 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
541 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
542 as some of the regions we are dragging may be on such tracks.
545 TrackViewList track_views = _editor->track_views;
546 track_views.sort (PresentationInfoTimeAxisViewSorter ());
548 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
549 _time_axis_views.push_back (*i);
551 TimeAxisView::Children children_list = (*i)->get_child_list ();
552 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
553 _time_axis_views.push_back (j->get());
557 /* the list of views can be empty at this point if this is a region list-insert drag
560 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
561 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
564 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
568 RegionDrag::region_going_away (RegionView* v)
570 list<DraggingView>::iterator i = _views.begin ();
571 while (i != _views.end() && i->view != v) {
575 if (i != _views.end()) {
580 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
581 * or -1 if it is not found.
584 RegionDrag::find_time_axis_view (TimeAxisView* t) const
587 int const N = _time_axis_views.size ();
588 while (i < N && _time_axis_views[i] != t) {
592 if (_time_axis_views[i] != t) {
599 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
600 : RegionDrag (e, i, p, v)
602 , _ignore_video_lock (false)
604 , _last_pointer_time_axis_view (0)
605 , _last_pointer_layer (0)
610 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
614 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
616 Drag::start_grab (event, cursor);
617 setup_snap_delta (_last_frame_position);
619 show_verbose_cursor_time (_last_frame_position);
621 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
623 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
624 assert(_last_pointer_time_axis_view >= 0);
625 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
628 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
629 _ignore_video_lock = true;
633 /* cross track dragging seems broken here. disabled for now. */
634 _y_constrained = true;
639 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
641 /* compute the amount of pointer motion in frames, and where
642 the region would be if we moved it by that much.
644 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
646 framepos_t sync_frame;
647 framecnt_t sync_offset;
650 sync_offset = _primary->region()->sync_offset (sync_dir);
652 /* we don't handle a sync point that lies before zero.
654 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
656 framecnt_t const sd = snap_delta (event->button.state);
657 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
659 _editor->snap_to_with_modifier (sync_frame, event);
661 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
664 *pending_region_position = _last_frame_position;
667 if (*pending_region_position > max_framepos - _primary->region()->length()) {
668 *pending_region_position = _last_frame_position;
673 bool const x_move_allowed = !_x_constrained;
675 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
677 /* x movement since last time (in pixels) */
678 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
680 /* total x movement */
681 framecnt_t total_dx = *pending_region_position;
682 if (regions_came_from_canvas()) {
683 total_dx = total_dx - grab_frame ();
686 /* check that no regions have gone off the start of the session */
687 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
688 if ((i->view->region()->position() + total_dx) < 0) {
690 *pending_region_position = _last_frame_position;
701 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
707 const int tavsize = _time_axis_views.size();
708 const int dt = delta > 0 ? +1 : -1;
710 int target = start + delta - skip;
712 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
713 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
715 while (current >= 0 && current != target) {
717 if (current < 0 && dt < 0) {
720 if (current >= tavsize && dt > 0) {
723 if (current < 0 || current >= tavsize) {
727 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
728 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
732 if (distance_only && current == start + delta) {
740 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
742 if (_y_constrained) {
746 const int tavsize = _time_axis_views.size();
747 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
748 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
749 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
751 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
752 /* already in the drop zone */
753 if (delta_track >= 0) {
754 /* downward motion - OK if others are still not in the dropzone */
763 } else if (n >= tavsize) {
764 /* downward motion into drop zone. That's fine. */
768 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
769 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
770 /* not a track, or the wrong type */
774 double const l = i->layer + delta_layer;
776 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
777 mode to allow the user to place a region below another on layer 0.
779 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
780 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
781 If it has, the layers will be munged later anyway, so it's ok.
787 /* all regions being dragged are ok with this change */
791 struct DraggingViewSorter {
792 bool operator() (const DraggingView& a, const DraggingView& b) {
793 return a.time_axis_view < b.time_axis_view;
798 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
800 double delta_layer = 0;
801 int delta_time_axis_view = 0;
802 int current_pointer_time_axis_view = -1;
804 assert (!_views.empty ());
806 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
808 /* Find the TimeAxisView that the pointer is now over */
809 const double cur_y = current_pointer_y ();
810 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
811 TimeAxisView* tv = r.first;
813 if (!tv && cur_y < 0) {
814 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
818 /* find drop-zone y-position */
819 Coord last_track_bottom_edge;
820 last_track_bottom_edge = 0;
821 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
822 if (!(*t)->hidden()) {
823 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
828 if (tv && tv->view()) {
829 /* the mouse is over a track */
830 double layer = r.second;
832 if (first_move && tv->view()->layer_display() == Stacked) {
833 tv->view()->set_layer_display (Expanded);
836 /* Here's the current pointer position in terms of time axis view and layer */
837 current_pointer_time_axis_view = find_time_axis_view (tv);
838 assert(current_pointer_time_axis_view >= 0);
840 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
842 /* Work out the change in y */
844 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
845 if (!rtv || !rtv->is_track()) {
846 /* ignore non-tracks early on. we can't move any regions on them */
847 } else if (_last_pointer_time_axis_view < 0) {
848 /* Was in the drop-zone, now over a track.
849 * Hence it must be an upward move (from the bottom)
851 * track_index is still -1, so delta must be set to
852 * move up the correct number of tracks from the bottom.
854 * This is necessary because steps may be skipped if
855 * the bottom-most track is not a valid target and/or
856 * if there are hidden tracks at the bottom.
857 * Hence the initial offset (_ddropzone) as well as the
858 * last valid pointer position (_pdropzone) need to be
859 * taken into account.
861 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
863 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
866 /* TODO needs adjustment per DraggingView,
868 * e.g. select one region on the top-layer of a track
869 * and one region which is at the bottom-layer of another track
872 * Indicated drop-zones and layering is wrong.
873 * and may infer additional layers on the target-track
874 * (depending how many layers the original track had).
876 * Or select two regions (different layers) on a same track,
877 * move across a non-layer track.. -> layering info is lost.
878 * on drop either of the regions may be on top.
880 * Proposed solution: screw it :) well,
881 * don't use delta_layer, use an absolute value
882 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
883 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
884 * 3) iterate over all DraggingView, find the one that is over the track with most layers
885 * 4) proportionally scale layer to layers available on target
887 delta_layer = current_pointer_layer - _last_pointer_layer;
890 /* for automation lanes, there is a TimeAxisView but no ->view()
891 * if (!tv) -> dropzone
893 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
894 /* Moving into the drop-zone.. */
895 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
896 /* delta_time_axis_view may not be sufficient to move into the DZ
897 * the mouse may enter it, but it may not be a valid move due to
900 * -> remember the delta needed to move into the dropzone
902 _ddropzone = delta_time_axis_view;
903 /* ..but subtract hidden tracks (or routes) at the bottom.
904 * we silently move mover them
906 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
907 - _time_axis_views.size();
909 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
910 /* move around inside the zone.
911 * This allows to move further down until all regions are in the zone.
913 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
914 assert(ptr_y >= last_track_bottom_edge);
915 assert(_ddropzone > 0);
917 /* calculate mouse position in 'tracks' below last track. */
918 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
919 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
921 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
923 delta_time_axis_view = dzpos - _pdropzone;
924 } else if (dzpos < _pdropzone && _ndropzone > 0) {
925 // move up inside the DZ
926 delta_time_axis_view = dzpos - _pdropzone;
930 /* Work out the change in x */
931 framepos_t pending_region_position;
932 double const x_delta = compute_x_delta (event, &pending_region_position);
933 _last_frame_position = pending_region_position;
935 /* calculate hidden tracks in current y-axis delta */
937 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
938 /* The mouse is more than one track below the dropzone.
939 * distance calculation is not needed (and would not work, either
940 * because the dropzone is "packed").
942 * Except when [partially] moving regions out of dropzone in a large step.
943 * (the mouse may or may not remain in the DZ)
944 * Hidden tracks at the bottom of the TAV need to be skipped.
946 * This also handles the case if the mouse entered the DZ
947 * in a large step (exessive delta), either due to fast-movement,
948 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
950 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
951 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
953 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
954 -_time_axis_views.size() - dt;
957 else if (_last_pointer_time_axis_view < 0) {
958 /* Moving out of the zone. Check for hidden tracks at the bottom. */
959 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
960 -_time_axis_views.size() - delta_time_axis_view;
962 /* calculate hidden tracks that are skipped by the pointer movement */
963 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
964 - _last_pointer_time_axis_view
965 - delta_time_axis_view;
968 /* Verify change in y */
969 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
970 /* this y movement is not allowed, so do no y movement this time */
971 delta_time_axis_view = 0;
976 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
977 /* haven't reached next snap point, and we're not switching
978 trackviews nor layers. nothing to do.
983 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
984 PlaylistDropzoneMap playlist_dropzone_map;
985 _ndropzone = 0; // number of elements currently in the dropzone
988 /* sort views by time_axis.
989 * This retains track order in the dropzone, regardless
990 * of actual selection order
992 _views.sort (DraggingViewSorter());
994 /* count number of distinct tracks of all regions
995 * being dragged, used for dropzone.
998 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
999 if (i->time_axis_view != prev_track) {
1000 prev_track = i->time_axis_view;
1006 _views.back().time_axis_view -
1007 _views.front().time_axis_view;
1009 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1010 - _views.back().time_axis_view;
1012 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1016 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1018 RegionView* rv = i->view;
1023 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1030 /* reparent the regionview into a group above all
1034 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1035 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1036 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1037 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1038 /* move the item so that it continues to appear at the
1039 same location now that its parent has changed.
1041 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1044 /* If we have moved tracks, we'll fudge the layer delta so that the
1045 region gets moved back onto layer 0 on its new track; this avoids
1046 confusion when dragging regions from non-zero layers onto different
1049 double this_delta_layer = delta_layer;
1050 if (delta_time_axis_view != 0) {
1051 this_delta_layer = - i->layer;
1054 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1056 int track_index = i->time_axis_view + this_delta_time_axis_view;
1057 assert(track_index >= 0);
1059 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1060 /* Track is in the Dropzone */
1062 i->time_axis_view = track_index;
1063 assert(i->time_axis_view >= (int) _time_axis_views.size());
1066 double yposition = 0;
1067 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1068 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1071 /* store index of each new playlist as a negative count, starting at -1 */
1073 if (pdz == playlist_dropzone_map.end()) {
1074 /* compute where this new track (which doesn't exist yet) will live
1077 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1079 /* How high is this region view ? */
1081 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1082 ArdourCanvas::Rect bbox;
1085 bbox = obbox.get ();
1088 last_track_bottom_edge += bbox.height();
1090 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1093 yposition = pdz->second;
1096 /* values are zero or negative, hence the use of min() */
1097 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1102 /* The TimeAxisView that this region is now over */
1103 TimeAxisView* current_tv = _time_axis_views[track_index];
1105 /* Ensure it is moved from stacked -> expanded if appropriate */
1106 if (current_tv->view()->layer_display() == Stacked) {
1107 current_tv->view()->set_layer_display (Expanded);
1110 /* We're only allowed to go -ve in layer on Expanded views */
1111 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1112 this_delta_layer = - i->layer;
1116 rv->set_height (current_tv->view()->child_height ());
1118 /* Update show/hidden status as the region view may have come from a hidden track,
1119 or have moved to one.
1121 if (current_tv->hidden ()) {
1122 rv->get_canvas_group()->hide ();
1124 rv->get_canvas_group()->show ();
1127 /* Update the DraggingView */
1128 i->time_axis_view = track_index;
1129 i->layer += this_delta_layer;
1132 _editor->mouse_brush_insert_region (rv, pending_region_position);
1136 /* Get the y coordinate of the top of the track that this region is now over */
1137 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1139 /* And adjust for the layer that it should be on */
1140 StreamView* cv = current_tv->view ();
1141 switch (cv->layer_display ()) {
1145 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1148 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1152 /* need to get the parent of the regionview
1153 * canvas group and get its position in
1154 * equivalent coordinate space as the trackview
1155 * we are now dragging over.
1158 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1163 /* Now move the region view */
1164 rv->move (x_delta, y_delta);
1166 } /* foreach region */
1168 _total_x_delta += x_delta;
1170 if (x_delta != 0 && !_brushing) {
1171 show_verbose_cursor_time (_last_frame_position);
1174 /* keep track of pointer movement */
1176 /* the pointer is currently over a time axis view */
1178 if (_last_pointer_time_axis_view < 0) {
1179 /* last motion event was not over a time axis view
1180 * or last y-movement out of the dropzone was not valid
1183 if (delta_time_axis_view < 0) {
1184 /* in the drop zone, moving up */
1186 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1187 * We do not use negative _last_pointer_time_axis_view because
1188 * the dropzone is "packed" (the actual track offset is ignored)
1190 * As opposed to the actual number
1191 * of elements in the dropzone (_ndropzone)
1192 * _pdropzone is not constrained. This is necessary
1193 * to allow moving multiple regions with y-distance
1196 * There can be 0 elements in the dropzone,
1197 * even though the drag-pointer is inside the DZ.
1200 * [ Audio-track, Midi-track, Audio-track, DZ ]
1201 * move regions from both audio tracks at the same time into the
1202 * DZ by grabbing the region in the bottom track.
1204 assert(current_pointer_time_axis_view >= 0);
1205 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1209 /* only move out of the zone if the movement is OK */
1210 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1211 assert(delta_time_axis_view < 0);
1212 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1213 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1214 * the current position can be calculated as follows:
1216 // a well placed oofus attack can still throw this off.
1217 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1218 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1221 /* last motion event was also over a time axis view */
1222 _last_pointer_time_axis_view += delta_time_axis_view;
1223 assert(_last_pointer_time_axis_view >= 0);
1228 /* the pointer is not over a time axis view */
1229 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1230 _pdropzone += delta_time_axis_view - delta_skip;
1231 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1234 _last_pointer_layer += delta_layer;
1238 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1240 if (_copy && first_move) {
1241 if (_x_constrained && !_brushing) {
1242 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1243 } else if (!_brushing) {
1244 _editor->begin_reversible_command (Operations::region_copy);
1245 } else if (_brushing) {
1246 _editor->begin_reversible_command (Operations::drag_region_brush);
1248 /* duplicate the regionview(s) and region(s) */
1250 list<DraggingView> new_regionviews;
1252 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1254 RegionView* rv = i->view;
1255 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1256 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1258 const boost::shared_ptr<const Region> original = rv->region();
1259 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true
1260 , _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->enable_display (false);
2860 rv->region()->playlist()->clear_owned_changes ();
2862 if (_operation == StartTrim) {
2863 rv->trim_front_starting ();
2866 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2869 arv->temporarily_hide_envelope ();
2873 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2874 insert_result = _editor->motion_frozen_playlists.insert (pl);
2876 if (insert_result.second) {
2882 bool non_overlap_trim = false;
2884 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2885 non_overlap_trim = true;
2888 /* contstrain trim to fade length */
2889 if (_preserve_fade_anchor) {
2890 switch (_operation) {
2892 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2893 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2895 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2896 if (ar->locked()) continue;
2897 framecnt_t len = ar->fade_in()->back()->when;
2898 if (len < dt) dt = min(dt, len);
2902 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2903 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2905 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2906 if (ar->locked()) continue;
2907 framecnt_t len = ar->fade_out()->back()->when;
2908 if (len < -dt) dt = max(dt, -len);
2917 switch (_operation) {
2919 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2920 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2921 , _editor->get_grid_music_divisions (event->button.state));
2923 if (changed && _preserve_fade_anchor) {
2924 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2926 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2927 framecnt_t len = ar->fade_in()->back()->when;
2928 framecnt_t diff = ar->first_frame() - i->initial_position;
2929 framepos_t new_length = len - diff;
2930 i->anchored_fade_length = min (ar->length(), new_length);
2931 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2932 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2939 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2940 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, _editor->get_grid_music_divisions (event->button.state));
2941 if (changed && _preserve_fade_anchor) {
2942 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2944 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2945 framecnt_t len = ar->fade_out()->back()->when;
2946 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2947 framepos_t new_length = len + diff;
2948 i->anchored_fade_length = min (ar->length(), new_length);
2949 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2950 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2958 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2960 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2961 i->view->move_contents (frame_delta);
2967 switch (_operation) {
2969 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2972 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2975 // show_verbose_cursor_time (frame_delta);
2981 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2983 if (movement_occurred) {
2984 motion (event, false);
2986 if (_operation == StartTrim) {
2987 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2989 /* This must happen before the region's StatefulDiffCommand is created, as it may
2990 `correct' (ahem) the region's _start from being negative to being zero. It
2991 needs to be zero in the undo record.
2993 i->view->trim_front_ending ();
2995 if (_preserve_fade_anchor) {
2996 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2998 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2999 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3000 ar->set_fade_in_length(i->anchored_fade_length);
3001 ar->set_fade_in_active(true);
3004 if (_jump_position_when_done) {
3005 i->view->region()->set_position (i->initial_position);
3008 } else if (_operation == EndTrim) {
3009 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3010 if (_preserve_fade_anchor) {
3011 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3013 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3014 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3015 ar->set_fade_out_length(i->anchored_fade_length);
3016 ar->set_fade_out_active(true);
3019 if (_jump_position_when_done) {
3020 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3025 if (!_views.empty()) {
3026 if (_operation == StartTrim) {
3027 _editor->maybe_locate_with_edit_preroll(
3028 _views.begin()->view->region()->position());
3030 if (_operation == EndTrim) {
3031 _editor->maybe_locate_with_edit_preroll(
3032 _views.begin()->view->region()->position() +
3033 _views.begin()->view->region()->length());
3037 if (!_editor->selection->selected (_primary)) {
3038 _primary->thaw_after_trim ();
3041 set<boost::shared_ptr<Playlist> > diffed_playlists;
3043 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3044 i->view->thaw_after_trim ();
3045 i->view->enable_display (true);
3047 /* Trimming one region may affect others on the playlist, so we need
3048 to get undo Commands from the whole playlist rather than just the
3049 region. Use diffed_playlists to make sure we don't diff a given
3050 playlist more than once.
3052 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3053 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3054 vector<Command*> cmds;
3056 _editor->session()->add_commands (cmds);
3057 diffed_playlists.insert (p);
3062 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3066 _editor->motion_frozen_playlists.clear ();
3067 _editor->commit_reversible_command();
3070 /* no mouse movement */
3071 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3072 _editor->point_trim (event, adjusted_current_frame (event));
3076 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3077 i->view->region()->resume_property_changes ();
3082 TrimDrag::aborted (bool movement_occurred)
3084 /* Our motion method is changing model state, so use the Undo system
3085 to cancel. Perhaps not ideal, as this will leave an Undo point
3086 behind which may be slightly odd from the user's point of view.
3091 if (movement_occurred) {
3095 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3096 i->view->region()->resume_property_changes ();
3101 TrimDrag::setup_pointer_frame_offset ()
3103 list<DraggingView>::iterator i = _views.begin ();
3104 while (i != _views.end() && i->view != _primary) {
3108 if (i == _views.end()) {
3112 switch (_operation) {
3114 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3117 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3124 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3127 , _old_snap_type (e->snap_type())
3128 , _old_snap_mode (e->snap_mode())
3131 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3132 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3134 _real_section = &_marker->meter();
3139 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3141 Drag::start_grab (event, cursor);
3142 show_verbose_cursor_time (adjusted_current_frame(event));
3146 MeterMarkerDrag::setup_pointer_frame_offset ()
3148 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3152 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3155 // create a dummy marker to catch events, then hide it.
3158 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3160 _marker = new MeterMarker (
3162 *_editor->meter_group,
3163 UIConfiguration::instance().color ("meter marker"),
3165 *new MeterSection (_marker->meter())
3168 /* use the new marker for the grab */
3169 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3172 TempoMap& map (_editor->session()->tempo_map());
3173 /* get current state */
3174 before_state = &map.get_state();
3177 _editor->begin_reversible_command (_("move meter mark"));
3179 _editor->begin_reversible_command (_("copy meter mark"));
3181 Timecode::BBT_Time bbt = _real_section->bbt();
3183 /* we can't add a meter where one currently exists */
3184 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3189 const double beat = map.beat_at_bbt (bbt);
3190 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3191 , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style());
3192 if (!_real_section) {
3198 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3199 if (_real_section->position_lock_style() != AudioTime) {
3200 _editor->set_snap_to (SnapToBar);
3201 _editor->set_snap_mode (SnapNormal);
3205 framepos_t pf = adjusted_current_frame (event);
3207 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3208 /* never snap to music for audio locked */
3209 pf = adjusted_current_frame (event, false);
3212 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3214 /* fake marker meeds to stay under the mouse, unlike the real one. */
3215 _marker->set_position (adjusted_current_frame (event, false));
3217 show_verbose_cursor_time (_real_section->frame());
3221 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3223 if (!movement_occurred) {
3224 if (was_double_click()) {
3225 _editor->edit_meter_marker (*_marker);
3230 /* reinstate old snap setting */
3231 _editor->set_snap_to (_old_snap_type);
3232 _editor->set_snap_mode (_old_snap_mode);
3234 TempoMap& map (_editor->session()->tempo_map());
3236 XMLNode &after = map.get_state();
3237 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3238 _editor->commit_reversible_command ();
3240 // delete the dummy marker we used for visual representation while moving.
3241 // a new visual marker will show up automatically.
3246 MeterMarkerDrag::aborted (bool moved)
3248 _marker->set_position (_marker->meter().frame ());
3250 /* reinstate old snap setting */
3251 _editor->set_snap_to (_old_snap_type);
3252 _editor->set_snap_mode (_old_snap_mode);
3254 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3255 // delete the dummy marker we used for visual representation while moving.
3256 // a new visual marker will show up automatically.
3261 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3266 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3268 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3269 _real_section = &_marker->tempo();
3270 _movable = _real_section->movable();
3275 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3277 Drag::start_grab (event, cursor);
3278 if (!_real_section->active()) {
3279 show_verbose_cursor_text (_("inactive"));
3281 show_verbose_cursor_time (adjusted_current_frame (event));
3286 TempoMarkerDrag::setup_pointer_frame_offset ()
3288 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3292 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3294 if (!_real_section->active()) {
3300 // mvc drag - create a dummy marker to catch events, hide it.
3303 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3305 TempoSection section (_marker->tempo());
3307 _marker = new TempoMarker (
3309 *_editor->tempo_group,
3310 UIConfiguration::instance().color ("tempo marker"),
3312 *new TempoSection (_marker->tempo())
3315 /* use the new marker for the grab */
3316 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3319 TempoMap& map (_editor->session()->tempo_map());
3320 /* get current state */
3321 before_state = &map.get_state();
3324 _editor->begin_reversible_command (_("move tempo mark"));
3327 const Tempo tempo (_marker->tempo());
3328 const framepos_t frame = adjusted_current_frame (event) + 1;
3329 const TempoSection::Type type = _real_section->type();
3331 _editor->begin_reversible_command (_("copy tempo mark"));
3333 if (_real_section->position_lock_style() == MusicTime) {
3334 _real_section = map.add_tempo (tempo, map.pulse_at_frame (frame), 0, type, MusicTime);
3336 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3339 if (!_real_section) {
3347 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3348 /* use vertical movement to alter tempo .. should be log */
3349 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3352 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3354 show_verbose_cursor_text (strs.str());
3356 } else if (_movable && !_real_section->locked_to_meter()) {
3357 const framepos_t pf = adjusted_current_frame (event);
3358 TempoMap& map (_editor->session()->tempo_map());
3360 /* snap to beat is 1, snap to bar is -1 (sorry) */
3361 int sub_num = _editor->get_grid_music_divisions (event->button.state);
3363 map.gui_move_tempo (_real_section, pf, sub_num);
3365 show_verbose_cursor_time (_real_section->frame());
3367 _marker->set_position (adjusted_current_frame (event, false));
3371 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3373 if (!_real_section->active()) {
3376 if (!movement_occurred) {
3377 if (was_double_click()) {
3378 _editor->edit_tempo_marker (*_marker);
3383 TempoMap& map (_editor->session()->tempo_map());
3385 XMLNode &after = map.get_state();
3386 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3387 _editor->commit_reversible_command ();
3389 // delete the dummy marker we used for visual representation while moving.
3390 // a new visual marker will show up automatically.
3395 TempoMarkerDrag::aborted (bool moved)
3397 _marker->set_position (_marker->tempo().frame());
3399 TempoMap& map (_editor->session()->tempo_map());
3400 map.set_state (*before_state, Stateful::current_state_version);
3401 // delete the dummy (hidden) marker we used for events while moving.
3406 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3412 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3417 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3419 Drag::start_grab (event, cursor);
3420 TempoMap& map (_editor->session()->tempo_map());
3421 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3424 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3425 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3426 show_verbose_cursor_text (sstr.str());
3427 finished (event, false);
3431 BBTRulerDrag::setup_pointer_frame_offset ()
3433 TempoMap& map (_editor->session()->tempo_map());
3434 const double beat_at_frame = map.beat_at_frame (raw_grab_frame());
3435 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3438 if (divisions > 0) {
3439 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3441 /* while it makes some sense for the user to determine the division to 'grab',
3442 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3443 and the result over steep tempo curves. Use sixteenths.
3445 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3448 _pulse = map.pulse_at_beat (beat);
3450 _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3455 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3457 TempoMap& map (_editor->session()->tempo_map());
3460 /* get current state */
3461 before_state = &map.get_state();
3462 _editor->begin_reversible_command (_("dilate tempo"));
3467 if (_editor->snap_musical()) {
3468 pf = adjusted_current_frame (event, false);
3470 pf = adjusted_current_frame (event);
3473 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3474 /* adjust previous tempo to match pointer frame */
3475 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3478 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3479 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3480 show_verbose_cursor_text (sstr.str());
3484 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3486 if (!movement_occurred) {
3490 TempoMap& map (_editor->session()->tempo_map());
3492 XMLNode &after = map.get_state();
3493 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3494 _editor->commit_reversible_command ();
3498 BBTRulerDrag::aborted (bool moved)
3501 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3506 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3507 : Drag (e, &c.track_canvas_item(), false)
3512 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3515 /** Do all the things we do when dragging the playhead to make it look as though
3516 * we have located, without actually doing the locate (because that would cause
3517 * the diskstream buffers to be refilled, which is too slow).
3520 CursorDrag::fake_locate (framepos_t t)
3522 if (_editor->session () == 0) {
3526 _editor->playhead_cursor->set_position (t);
3528 Session* s = _editor->session ();
3529 if (s->timecode_transmission_suspended ()) {
3530 framepos_t const f = _editor->playhead_cursor->current_frame ();
3531 /* This is asynchronous so it will be sent "now"
3533 s->send_mmc_locate (f);
3534 /* These are synchronous and will be sent during the next
3537 s->queue_full_time_code ();
3538 s->queue_song_position_pointer ();
3541 show_verbose_cursor_time (t);
3542 _editor->UpdateAllTransportClocks (t);
3546 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3548 Drag::start_grab (event, c);
3549 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3551 _grab_zoom = _editor->samples_per_pixel;
3553 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3555 _editor->snap_to_with_modifier (where, event);
3557 _editor->_dragging_playhead = true;
3559 Session* s = _editor->session ();
3561 /* grab the track canvas item as well */
3563 _cursor.track_canvas_item().grab();
3566 if (_was_rolling && _stop) {
3570 if (s->is_auditioning()) {
3571 s->cancel_audition ();
3575 if (AudioEngine::instance()->connected()) {
3577 /* do this only if we're the engine is connected
3578 * because otherwise this request will never be
3579 * serviced and we'll busy wait forever. likewise,
3580 * notice if we are disconnected while waiting for the
3581 * request to be serviced.
3584 s->request_suspend_timecode_transmission ();
3585 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3586 /* twiddle our thumbs */
3591 fake_locate (where - snap_delta (event->button.state));
3595 CursorDrag::motion (GdkEvent* event, bool)
3597 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3598 _editor->snap_to_with_modifier (where, event);
3599 if (where != last_pointer_frame()) {
3600 fake_locate (where - snap_delta (event->button.state));
3605 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3607 _editor->_dragging_playhead = false;
3609 _cursor.track_canvas_item().ungrab();
3611 if (!movement_occurred && _stop) {
3615 motion (event, false);
3617 Session* s = _editor->session ();
3619 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3620 _editor->_pending_locate_request = true;
3621 s->request_resume_timecode_transmission ();
3626 CursorDrag::aborted (bool)
3628 _cursor.track_canvas_item().ungrab();
3630 if (_editor->_dragging_playhead) {
3631 _editor->session()->request_resume_timecode_transmission ();
3632 _editor->_dragging_playhead = false;
3635 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3638 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3639 : RegionDrag (e, i, p, v)
3641 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3645 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3647 Drag::start_grab (event, cursor);
3649 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3650 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3651 setup_snap_delta (r->position ());
3653 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3657 FadeInDrag::setup_pointer_frame_offset ()
3659 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3660 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3661 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3665 FadeInDrag::motion (GdkEvent* event, bool)
3667 framecnt_t fade_length;
3669 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3670 _editor->snap_to_with_modifier (pos, event);
3671 pos -= snap_delta (event->button.state);
3673 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3675 if (pos < (region->position() + 64)) {
3676 fade_length = 64; // this should be a minimum defined somewhere
3677 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3678 fade_length = region->length() - region->fade_out()->back()->when - 1;
3680 fade_length = pos - region->position();
3683 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3685 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3691 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3694 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3698 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3700 if (!movement_occurred) {
3704 framecnt_t fade_length;
3705 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3706 _editor->snap_to_with_modifier (pos, event);
3707 pos -= snap_delta (event->button.state);
3709 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3711 if (pos < (region->position() + 64)) {
3712 fade_length = 64; // this should be a minimum defined somewhere
3713 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3714 fade_length = region->length() - region->fade_out()->back()->when - 1;
3716 fade_length = pos - region->position();
3719 bool in_command = false;
3721 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3723 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3729 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3730 XMLNode &before = alist->get_state();
3732 tmp->audio_region()->set_fade_in_length (fade_length);
3733 tmp->audio_region()->set_fade_in_active (true);
3736 _editor->begin_reversible_command (_("change fade in length"));
3739 XMLNode &after = alist->get_state();
3740 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3744 _editor->commit_reversible_command ();
3749 FadeInDrag::aborted (bool)
3751 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3752 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3758 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3762 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3763 : RegionDrag (e, i, p, v)
3765 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3769 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3771 Drag::start_grab (event, cursor);
3773 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3774 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3775 setup_snap_delta (r->last_frame ());
3777 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3781 FadeOutDrag::setup_pointer_frame_offset ()
3783 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3784 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3785 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3789 FadeOutDrag::motion (GdkEvent* event, bool)
3791 framecnt_t fade_length;
3793 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3794 _editor->snap_to_with_modifier (pos, event);
3795 pos -= snap_delta (event->button.state);
3797 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3799 if (pos > (region->last_frame() - 64)) {
3800 fade_length = 64; // this should really be a minimum fade defined somewhere
3801 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3802 fade_length = region->length() - region->fade_in()->back()->when - 1;
3804 fade_length = region->last_frame() - pos;
3807 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3809 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3815 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3818 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3822 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3824 if (!movement_occurred) {
3828 framecnt_t fade_length;
3830 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3831 _editor->snap_to_with_modifier (pos, event);
3832 pos -= snap_delta (event->button.state);
3834 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3836 if (pos > (region->last_frame() - 64)) {
3837 fade_length = 64; // this should really be a minimum fade defined somewhere
3838 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3839 fade_length = region->length() - region->fade_in()->back()->when - 1;
3841 fade_length = region->last_frame() - pos;
3844 bool in_command = false;
3846 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3848 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3854 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3855 XMLNode &before = alist->get_state();
3857 tmp->audio_region()->set_fade_out_length (fade_length);
3858 tmp->audio_region()->set_fade_out_active (true);
3861 _editor->begin_reversible_command (_("change fade out length"));
3864 XMLNode &after = alist->get_state();
3865 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3869 _editor->commit_reversible_command ();
3874 FadeOutDrag::aborted (bool)
3876 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3877 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3883 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3887 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3889 , _selection_changed (false)
3891 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3892 Gtk::Window* toplevel = _editor->current_toplevel();
3893 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3897 _points.push_back (ArdourCanvas::Duple (0, 0));
3899 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3902 MarkerDrag::~MarkerDrag ()
3904 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3909 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3911 location = new Location (*l);
3912 markers.push_back (m);
3917 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3919 Drag::start_grab (event, cursor);
3923 Location *location = _editor->find_location_from_marker (_marker, is_start);
3924 _editor->_dragging_edit_point = true;
3926 update_item (location);
3928 // _drag_line->show();
3929 // _line->raise_to_top();
3932 show_verbose_cursor_time (location->start());
3934 show_verbose_cursor_time (location->end());
3936 setup_snap_delta (is_start ? location->start() : location->end());
3938 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3941 case Selection::Toggle:
3942 /* we toggle on the button release */
3944 case Selection::Set:
3945 if (!_editor->selection->selected (_marker)) {
3946 _editor->selection->set (_marker);
3947 _selection_changed = true;
3950 case Selection::Extend:
3952 Locations::LocationList ll;
3953 list<ArdourMarker*> to_add;
3955 _editor->selection->markers.range (s, e);
3956 s = min (_marker->position(), s);
3957 e = max (_marker->position(), e);
3960 if (e < max_framepos) {
3963 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3964 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3965 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3968 to_add.push_back (lm->start);
3971 to_add.push_back (lm->end);
3975 if (!to_add.empty()) {
3976 _editor->selection->add (to_add);
3977 _selection_changed = true;
3981 case Selection::Add:
3982 _editor->selection->add (_marker);
3983 _selection_changed = true;
3988 /* Set up copies for us to manipulate during the drag
3991 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3993 Location* l = _editor->find_location_from_marker (*i, is_start);
4000 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4002 /* range: check that the other end of the range isn't
4005 CopiedLocationInfo::iterator x;
4006 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4007 if (*(*x).location == *l) {
4011 if (x == _copied_locations.end()) {
4012 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4014 (*x).markers.push_back (*i);
4015 (*x).move_both = true;
4023 MarkerDrag::setup_pointer_frame_offset ()
4026 Location *location = _editor->find_location_from_marker (_marker, is_start);
4027 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4031 MarkerDrag::motion (GdkEvent* event, bool)
4033 framecnt_t f_delta = 0;
4035 bool move_both = false;
4036 Location *real_location;
4037 Location *copy_location = 0;
4038 framecnt_t const sd = snap_delta (event->button.state);
4040 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4041 framepos_t next = newframe;
4043 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4047 CopiedLocationInfo::iterator x;
4049 /* find the marker we're dragging, and compute the delta */
4051 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4053 copy_location = (*x).location;
4055 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4057 /* this marker is represented by this
4058 * CopiedLocationMarkerInfo
4061 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4066 if (real_location->is_mark()) {
4067 f_delta = newframe - copy_location->start();
4071 switch (_marker->type()) {
4072 case ArdourMarker::SessionStart:
4073 case ArdourMarker::RangeStart:
4074 case ArdourMarker::LoopStart:
4075 case ArdourMarker::PunchIn:
4076 f_delta = newframe - copy_location->start();
4079 case ArdourMarker::SessionEnd:
4080 case ArdourMarker::RangeEnd:
4081 case ArdourMarker::LoopEnd:
4082 case ArdourMarker::PunchOut:
4083 f_delta = newframe - copy_location->end();
4086 /* what kind of marker is this ? */
4095 if (x == _copied_locations.end()) {
4096 /* hmm, impossible - we didn't find the dragged marker */
4100 /* now move them all */
4102 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4104 copy_location = x->location;
4106 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4110 if (real_location->locked()) {
4114 if (copy_location->is_mark()) {
4118 copy_location->set_start (copy_location->start() + f_delta);
4122 framepos_t new_start = copy_location->start() + f_delta;
4123 framepos_t new_end = copy_location->end() + f_delta;
4125 if (is_start) { // start-of-range marker
4127 if (move_both || (*x).move_both) {
4128 copy_location->set_start (new_start);
4129 copy_location->set_end (new_end);
4130 } else if (new_start < copy_location->end()) {
4131 copy_location->set_start (new_start);
4132 } else if (newframe > 0) {
4133 //_editor->snap_to (next, RoundUpAlways, true);
4134 copy_location->set_end (next);
4135 copy_location->set_start (newframe);
4138 } else { // end marker
4140 if (move_both || (*x).move_both) {
4141 copy_location->set_end (new_end);
4142 copy_location->set_start (new_start);
4143 } else if (new_end > copy_location->start()) {
4144 copy_location->set_end (new_end);
4145 } else if (newframe > 0) {
4146 //_editor->snap_to (next, RoundDownAlways, true);
4147 copy_location->set_start (next);
4148 copy_location->set_end (newframe);
4153 update_item (copy_location);
4155 /* now lookup the actual GUI items used to display this
4156 * location and move them to wherever the copy of the location
4157 * is now. This means that the logic in ARDOUR::Location is
4158 * still enforced, even though we are not (yet) modifying
4159 * the real Location itself.
4162 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4165 lm->set_position (copy_location->start(), copy_location->end());
4170 assert (!_copied_locations.empty());
4172 show_verbose_cursor_time (newframe);
4176 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4178 if (!movement_occurred) {
4180 if (was_double_click()) {
4181 _editor->rename_marker (_marker);
4185 /* just a click, do nothing but finish
4186 off the selection process
4189 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4191 case Selection::Set:
4192 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4193 _editor->selection->set (_marker);
4194 _selection_changed = true;
4198 case Selection::Toggle:
4199 /* we toggle on the button release, click only */
4200 _editor->selection->toggle (_marker);
4201 _selection_changed = true;
4205 case Selection::Extend:
4206 case Selection::Add:
4210 if (_selection_changed) {
4211 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4212 _editor->commit_reversible_selection_op();
4218 _editor->_dragging_edit_point = false;
4220 XMLNode &before = _editor->session()->locations()->get_state();
4221 bool in_command = false;
4223 MarkerSelection::iterator i;
4224 CopiedLocationInfo::iterator x;
4227 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4228 x != _copied_locations.end() && i != _editor->selection->markers.end();
4231 Location * location = _editor->find_location_from_marker (*i, is_start);
4235 if (location->locked()) {
4239 _editor->begin_reversible_command ( _("move marker") );
4242 if (location->is_mark()) {
4243 location->set_start (((*x).location)->start());
4245 location->set (((*x).location)->start(), ((*x).location)->end());
4248 if (location->is_session_range()) {
4249 _editor->session()->set_end_is_free (false);
4255 XMLNode &after = _editor->session()->locations()->get_state();
4256 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4257 _editor->commit_reversible_command ();
4262 MarkerDrag::aborted (bool movement_occurred)
4264 if (!movement_occurred) {
4268 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4270 /* move all markers to their original location */
4273 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4276 Location * location = _editor->find_location_from_marker (*m, is_start);
4279 (*m)->set_position (is_start ? location->start() : location->end());
4286 MarkerDrag::update_item (Location*)
4291 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4293 , _fixed_grab_x (0.0)
4294 , _fixed_grab_y (0.0)
4295 , _cumulative_x_drag (0.0)
4296 , _cumulative_y_drag (0.0)
4300 if (_zero_gain_fraction < 0.0) {
4301 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4304 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4306 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4312 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4314 Drag::start_grab (event, _editor->cursors()->fader);
4316 // start the grab at the center of the control point so
4317 // the point doesn't 'jump' to the mouse after the first drag
4318 _fixed_grab_x = _point->get_x();
4319 _fixed_grab_y = _point->get_y();
4321 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4322 setup_snap_delta (pos);
4324 float const fraction = 1 - (_point->get_y() / _point->line().height());
4325 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4327 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4329 if (!_point->can_slide ()) {
4330 _x_constrained = true;
4335 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4337 double dx = _drags->current_pointer_x() - last_pointer_x();
4338 double dy = current_pointer_y() - last_pointer_y();
4339 bool need_snap = true;
4341 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4347 /* coordinate in pixels relative to the start of the region (for region-based automation)
4348 or track (for track-based automation) */
4349 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4350 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4352 // calculate zero crossing point. back off by .01 to stay on the
4353 // positive side of zero
4354 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4356 if (_x_constrained) {
4359 if (_y_constrained) {
4363 _cumulative_x_drag = cx - _fixed_grab_x;
4364 _cumulative_y_drag = cy - _fixed_grab_y;
4368 cy = min ((double) _point->line().height(), cy);
4370 // make sure we hit zero when passing through
4371 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4375 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4377 if (!_x_constrained && need_snap) {
4378 _editor->snap_to_with_modifier (cx_frames, event);
4381 cx_frames -= snap_delta (event->button.state);
4382 cx_frames = min (cx_frames, _point->line().maximum_time());
4384 float const fraction = 1.0 - (cy / _point->line().height());
4387 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4388 _editor->begin_reversible_command (_("automation event move"));
4389 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4391 pair<double, float> result;
4392 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4394 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4398 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4400 if (!movement_occurred) {
4403 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4404 _editor->reset_point_selection ();
4408 _point->line().end_drag (_pushing, _final_index);
4409 _editor->commit_reversible_command ();
4414 ControlPointDrag::aborted (bool)
4416 _point->line().reset ();
4420 ControlPointDrag::active (Editing::MouseMode m)
4422 if (m == Editing::MouseDraw) {
4423 /* always active in mouse draw */
4427 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4428 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4431 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4434 , _fixed_grab_x (0.0)
4435 , _fixed_grab_y (0.0)
4436 , _cumulative_y_drag (0)
4440 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4444 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4446 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4449 _item = &_line->grab_item ();
4451 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4452 origin, and ditto for y.
4455 double mx = event->button.x;
4456 double my = event->button.y;
4458 _line->grab_item().canvas_to_item (mx, my);
4460 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4462 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4463 /* no adjacent points */
4467 Drag::start_grab (event, _editor->cursors()->fader);
4469 /* store grab start in item frame */
4470 double const bx = _line->nth (_before)->get_x();
4471 double const ax = _line->nth (_after)->get_x();
4472 double const click_ratio = (ax - mx) / (ax - bx);
4474 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4479 double fraction = 1.0 - (cy / _line->height());
4481 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4485 LineDrag::motion (GdkEvent* event, bool first_move)
4487 double dy = current_pointer_y() - last_pointer_y();
4489 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4493 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4495 _cumulative_y_drag = cy - _fixed_grab_y;
4498 cy = min ((double) _line->height(), cy);
4500 double const fraction = 1.0 - (cy / _line->height());
4504 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4506 _editor->begin_reversible_command (_("automation range move"));
4507 _line->start_drag_line (_before, _after, initial_fraction);
4510 /* we are ignoring x position for this drag, so we can just pass in anything */
4511 pair<double, float> result;
4513 result = _line->drag_motion (0, fraction, true, false, ignored);
4514 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4518 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4520 if (movement_occurred) {
4521 motion (event, false);
4522 _line->end_drag (false, 0);
4523 _editor->commit_reversible_command ();
4525 /* add a new control point on the line */
4527 AutomationTimeAxisView* atv;
4529 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4530 framepos_t where = grab_frame ();
4533 double cy = _fixed_grab_y;
4535 _line->grab_item().item_to_canvas (cx, cy);
4537 atv->add_automation_event (event, where, cy, false);
4538 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4539 AudioRegionView* arv;
4541 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4542 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4549 LineDrag::aborted (bool)
4554 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4558 _region_view_grab_x (0.0),
4559 _cumulative_x_drag (0),
4563 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4567 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4569 Drag::start_grab (event);
4571 _line = reinterpret_cast<Line*> (_item);
4574 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4576 double cx = event->button.x;
4577 double cy = event->button.y;
4579 _item->parent()->canvas_to_item (cx, cy);
4581 /* store grab start in parent frame */
4582 _region_view_grab_x = cx;
4584 _before = *(float*) _item->get_data ("position");
4586 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4588 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4592 FeatureLineDrag::motion (GdkEvent*, bool)
4594 double dx = _drags->current_pointer_x() - last_pointer_x();
4596 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4598 _cumulative_x_drag += dx;
4600 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4609 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4611 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4613 float *pos = new float;
4616 _line->set_data ("position", pos);
4622 FeatureLineDrag::finished (GdkEvent*, bool)
4624 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4625 _arv->update_transient(_before, _before);
4629 FeatureLineDrag::aborted (bool)
4634 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4636 , _vertical_only (false)
4638 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4642 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4644 Drag::start_grab (event);
4645 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4649 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4656 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4658 framepos_t grab = grab_frame ();
4659 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4660 _editor->snap_to_with_modifier (grab, event);
4662 grab = raw_grab_frame ();
4665 /* base start and end on initial click position */
4675 if (current_pointer_y() < grab_y()) {
4676 y1 = current_pointer_y();
4679 y2 = current_pointer_y();
4683 if (start != end || y1 != y2) {
4685 double x1 = _editor->sample_to_pixel (start);
4686 double x2 = _editor->sample_to_pixel (end);
4687 const double min_dimension = 2.0;
4689 if (_vertical_only) {
4690 /* fixed 10 pixel width */
4694 x2 = min (x1 - min_dimension, x2);
4696 x2 = max (x1 + min_dimension, x2);
4701 y2 = min (y1 - min_dimension, y2);
4703 y2 = max (y1 + min_dimension, y2);
4706 /* translate rect into item space and set */
4708 ArdourCanvas::Rect r (x1, y1, x2, y2);
4710 /* this drag is a _trackview_only == true drag, so the y1 and
4711 * y2 (computed using current_pointer_y() and grab_y()) will be
4712 * relative to the top of the trackview group). The
4713 * rubberband rect has the same parent/scroll offset as the
4714 * the trackview group, so we can use the "r" rect directly
4715 * to set the shape of the rubberband.
4718 _editor->rubberband_rect->set (r);
4719 _editor->rubberband_rect->show();
4720 _editor->rubberband_rect->raise_to_top();
4722 show_verbose_cursor_time (pf);
4724 do_select_things (event, true);
4729 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4733 framepos_t grab = grab_frame ();
4734 framepos_t lpf = last_pointer_frame ();
4736 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4737 grab = raw_grab_frame ();
4738 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4752 if (current_pointer_y() < grab_y()) {
4753 y1 = current_pointer_y();
4756 y2 = current_pointer_y();
4760 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4764 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4766 if (movement_occurred) {
4768 motion (event, false);
4769 do_select_things (event, false);
4775 bool do_deselect = true;
4776 MidiTimeAxisView* mtv;
4778 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4780 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4781 /* nothing selected */
4782 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4783 do_deselect = false;
4787 /* do not deselect if Primary or Tertiary (toggle-select or
4788 * extend-select are pressed.
4791 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4792 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4799 _editor->rubberband_rect->hide();
4803 RubberbandSelectDrag::aborted (bool)
4805 _editor->rubberband_rect->hide ();
4808 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4809 : RegionDrag (e, i, p, v)
4811 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4815 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4817 Drag::start_grab (event, cursor);
4819 _editor->get_selection().add (_primary);
4821 framepos_t where = _primary->region()->position();
4822 setup_snap_delta (where);
4824 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4828 TimeFXDrag::motion (GdkEvent* event, bool)
4830 RegionView* rv = _primary;
4831 StreamView* cv = rv->get_time_axis_view().view ();
4833 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4834 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4835 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4836 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4837 _editor->snap_to_with_modifier (pf, event);
4838 pf -= snap_delta (event->button.state);
4840 if (pf > rv->region()->position()) {
4841 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4844 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4848 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4850 /* this may have been a single click, no drag. We still want the dialog
4851 to show up in that case, so that the user can manually edit the
4852 parameters for the timestretch.
4855 float fraction = 1.0;
4857 if (movement_occurred) {
4859 motion (event, false);
4861 _primary->get_time_axis_view().hide_timestretch ();
4863 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4865 if (adjusted_frame_pos < _primary->region()->position()) {
4866 /* backwards drag of the left edge - not usable */
4870 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4872 fraction = (double) newlen / (double) _primary->region()->length();
4874 #ifndef USE_RUBBERBAND
4875 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4876 if (_primary->region()->data_type() == DataType::AUDIO) {
4877 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4882 if (!_editor->get_selection().regions.empty()) {
4883 /* primary will already be included in the selection, and edit
4884 group shared editing will propagate selection across
4885 equivalent regions, so just use the current region
4889 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4890 error << _("An error occurred while executing time stretch operation") << endmsg;
4896 TimeFXDrag::aborted (bool)
4898 _primary->get_time_axis_view().hide_timestretch ();
4901 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4904 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4908 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4910 Drag::start_grab (event);
4914 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4916 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4920 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4922 if (movement_occurred && _editor->session()) {
4923 /* make sure we stop */
4924 _editor->session()->request_transport_speed (0.0);
4929 ScrubDrag::aborted (bool)
4934 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4938 , _time_selection_at_start (!_editor->get_selection().time.empty())
4940 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4942 if (_time_selection_at_start) {
4943 start_at_start = _editor->get_selection().time.start();
4944 end_at_start = _editor->get_selection().time.end_frame();
4949 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4951 if (_editor->session() == 0) {
4955 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4957 switch (_operation) {
4958 case CreateSelection:
4959 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4964 cursor = _editor->cursors()->selector;
4965 Drag::start_grab (event, cursor);
4968 case SelectionStartTrim:
4969 if (_editor->clicked_axisview) {
4970 _editor->clicked_axisview->order_selection_trims (_item, true);
4972 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4975 case SelectionEndTrim:
4976 if (_editor->clicked_axisview) {
4977 _editor->clicked_axisview->order_selection_trims (_item, false);
4979 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4983 Drag::start_grab (event, cursor);
4986 case SelectionExtend:
4987 Drag::start_grab (event, cursor);
4991 if (_operation == SelectionMove) {
4992 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4994 show_verbose_cursor_time (adjusted_current_frame (event));
4999 SelectionDrag::setup_pointer_frame_offset ()
5001 switch (_operation) {
5002 case CreateSelection:
5003 _pointer_frame_offset = 0;
5006 case SelectionStartTrim:
5008 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5011 case SelectionEndTrim:
5012 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5015 case SelectionExtend:
5021 SelectionDrag::motion (GdkEvent* event, bool first_move)
5023 framepos_t start = 0;
5025 framecnt_t length = 0;
5026 framecnt_t distance = 0;
5028 framepos_t const pending_position = adjusted_current_frame (event);
5030 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5034 switch (_operation) {
5035 case CreateSelection:
5037 framepos_t grab = grab_frame ();
5040 grab = adjusted_current_frame (event, false);
5041 if (grab < pending_position) {
5042 _editor->snap_to (grab, RoundDownMaybe);
5044 _editor->snap_to (grab, RoundUpMaybe);
5048 if (pending_position < grab) {
5049 start = pending_position;
5052 end = pending_position;
5056 /* first drag: Either add to the selection
5057 or create a new selection
5064 /* adding to the selection */
5065 _editor->set_selected_track_as_side_effect (Selection::Add);
5066 _editor->clicked_selection = _editor->selection->add (start, end);
5073 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5074 _editor->set_selected_track_as_side_effect (Selection::Set);
5077 _editor->clicked_selection = _editor->selection->set (start, end);
5081 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5082 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5083 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5085 _editor->selection->add (atest);
5089 /* select all tracks within the rectangle that we've marked out so far */
5090 TrackViewList new_selection;
5091 TrackViewList& all_tracks (_editor->track_views);
5093 ArdourCanvas::Coord const top = grab_y();
5094 ArdourCanvas::Coord const bottom = current_pointer_y();
5096 if (top >= 0 && bottom >= 0) {
5098 //first, find the tracks that are covered in the y range selection
5099 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5100 if ((*i)->covered_by_y_range (top, bottom)) {
5101 new_selection.push_back (*i);
5105 //now find any tracks that are GROUPED with the tracks we selected
5106 TrackViewList grouped_add = new_selection;
5107 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5108 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5109 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5110 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5111 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5112 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5113 grouped_add.push_back (*j);
5118 //now compare our list with the current selection, and add or remove as necessary
5119 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5120 TrackViewList tracks_to_add;
5121 TrackViewList tracks_to_remove;
5122 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5123 if ( !_editor->selection->tracks.contains ( *i ) )
5124 tracks_to_add.push_back ( *i );
5125 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5126 if ( !grouped_add.contains ( *i ) )
5127 tracks_to_remove.push_back ( *i );
5128 _editor->selection->add(tracks_to_add);
5129 _editor->selection->remove(tracks_to_remove);
5135 case SelectionStartTrim:
5137 end = _editor->selection->time[_editor->clicked_selection].end;
5139 if (pending_position > end) {
5142 start = pending_position;
5146 case SelectionEndTrim:
5148 start = _editor->selection->time[_editor->clicked_selection].start;
5150 if (pending_position < start) {
5153 end = pending_position;
5160 start = _editor->selection->time[_editor->clicked_selection].start;
5161 end = _editor->selection->time[_editor->clicked_selection].end;
5163 length = end - start;
5164 distance = pending_position - start;
5165 start = pending_position;
5166 _editor->snap_to (start);
5168 end = start + length;
5172 case SelectionExtend:
5177 switch (_operation) {
5179 if (_time_selection_at_start) {
5180 _editor->selection->move_time (distance);
5184 _editor->selection->replace (_editor->clicked_selection, start, end);
5188 if (_operation == SelectionMove) {
5189 show_verbose_cursor_time(start);
5191 show_verbose_cursor_time(pending_position);
5196 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5198 Session* s = _editor->session();
5200 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5201 if (movement_occurred) {
5202 motion (event, false);
5203 /* XXX this is not object-oriented programming at all. ick */
5204 if (_editor->selection->time.consolidate()) {
5205 _editor->selection->TimeChanged ();
5208 /* XXX what if its a music time selection? */
5210 if (s->get_play_range() && s->transport_rolling()) {
5211 s->request_play_range (&_editor->selection->time, true);
5212 } else if (!s->config.get_external_sync()) {
5213 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5214 if (_operation == SelectionEndTrim)
5215 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5217 s->request_locate (_editor->get_selection().time.start());
5221 if (_editor->get_selection().time.length() != 0) {
5222 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5224 s->clear_range_selection ();
5229 /* just a click, no pointer movement.
5232 if (_operation == SelectionExtend) {
5233 if (_time_selection_at_start) {
5234 framepos_t pos = adjusted_current_frame (event, false);
5235 framepos_t start = min (pos, start_at_start);
5236 framepos_t end = max (pos, end_at_start);
5237 _editor->selection->set (start, end);
5240 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5241 if (_editor->clicked_selection) {
5242 _editor->selection->remove (_editor->clicked_selection);
5245 if (!_editor->clicked_selection) {
5246 _editor->selection->clear_time();
5251 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5252 _editor->selection->set (_editor->clicked_axisview);
5255 if (s && s->get_play_range () && s->transport_rolling()) {
5256 s->request_stop (false, false);
5261 _editor->stop_canvas_autoscroll ();
5262 _editor->clicked_selection = 0;
5263 _editor->commit_reversible_selection_op ();
5267 SelectionDrag::aborted (bool)
5272 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5273 : Drag (e, i, false),
5277 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5279 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5280 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5281 physical_screen_height (_editor->current_toplevel()->get_window())));
5282 _drag_rect->hide ();
5284 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5285 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5288 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5290 /* normal canvas items will be cleaned up when their parent group is deleted. But
5291 this item is created as the child of a long-lived parent group, and so we
5292 need to explicitly delete it.
5298 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5300 if (_editor->session() == 0) {
5304 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5306 if (!_editor->temp_location) {
5307 _editor->temp_location = new Location (*_editor->session());
5310 switch (_operation) {
5311 case CreateSkipMarker:
5312 case CreateRangeMarker:
5313 case CreateTransportMarker:
5314 case CreateCDMarker:
5316 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5321 cursor = _editor->cursors()->selector;
5325 Drag::start_grab (event, cursor);
5327 show_verbose_cursor_time (adjusted_current_frame (event));
5331 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5333 framepos_t start = 0;
5335 ArdourCanvas::Rectangle *crect;
5337 switch (_operation) {
5338 case CreateSkipMarker:
5339 crect = _editor->range_bar_drag_rect;
5341 case CreateRangeMarker:
5342 crect = _editor->range_bar_drag_rect;
5344 case CreateTransportMarker:
5345 crect = _editor->transport_bar_drag_rect;
5347 case CreateCDMarker:
5348 crect = _editor->cd_marker_bar_drag_rect;
5351 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5356 framepos_t const pf = adjusted_current_frame (event);
5358 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5359 framepos_t grab = grab_frame ();
5360 _editor->snap_to (grab);
5362 if (pf < grab_frame()) {
5370 /* first drag: Either add to the selection
5371 or create a new selection.
5376 _editor->temp_location->set (start, end);
5380 update_item (_editor->temp_location);
5382 //_drag_rect->raise_to_top();
5388 _editor->temp_location->set (start, end);
5390 double x1 = _editor->sample_to_pixel (start);
5391 double x2 = _editor->sample_to_pixel (end);
5395 update_item (_editor->temp_location);
5398 show_verbose_cursor_time (pf);
5403 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5405 Location * newloc = 0;
5409 if (movement_occurred) {
5410 motion (event, false);
5413 switch (_operation) {
5414 case CreateSkipMarker:
5415 case CreateRangeMarker:
5416 case CreateCDMarker:
5418 XMLNode &before = _editor->session()->locations()->get_state();
5419 if (_operation == CreateSkipMarker) {
5420 _editor->begin_reversible_command (_("new skip marker"));
5421 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5422 flags = Location::IsRangeMarker | Location::IsSkip;
5423 _editor->range_bar_drag_rect->hide();
5424 } else if (_operation == CreateCDMarker) {
5425 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5426 _editor->begin_reversible_command (_("new CD marker"));
5427 flags = Location::IsRangeMarker | Location::IsCDMarker;
5428 _editor->cd_marker_bar_drag_rect->hide();
5430 _editor->begin_reversible_command (_("new skip marker"));
5431 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5432 flags = Location::IsRangeMarker;
5433 _editor->range_bar_drag_rect->hide();
5435 newloc = new Location (
5436 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5439 _editor->session()->locations()->add (newloc, true);
5440 XMLNode &after = _editor->session()->locations()->get_state();
5441 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5442 _editor->commit_reversible_command ();
5446 case CreateTransportMarker:
5447 // popup menu to pick loop or punch
5448 _editor->new_transport_marker_context_menu (&event->button, _item);
5454 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5456 if (_operation == CreateTransportMarker) {
5458 /* didn't drag, so just locate */
5460 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5462 } else if (_operation == CreateCDMarker) {
5464 /* didn't drag, but mark is already created so do
5467 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5472 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5474 if (end == max_framepos) {
5475 end = _editor->session()->current_end_frame ();
5478 if (start == max_framepos) {
5479 start = _editor->session()->current_start_frame ();
5482 switch (_editor->mouse_mode) {
5484 /* find the two markers on either side and then make the selection from it */
5485 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5489 /* find the two markers on either side of the click and make the range out of it */
5490 _editor->selection->set (start, end);
5499 _editor->stop_canvas_autoscroll ();
5503 RangeMarkerBarDrag::aborted (bool movement_occurred)
5505 if (movement_occurred) {
5506 _drag_rect->hide ();
5511 RangeMarkerBarDrag::update_item (Location* location)
5513 double const x1 = _editor->sample_to_pixel (location->start());
5514 double const x2 = _editor->sample_to_pixel (location->end());
5516 _drag_rect->set_x0 (x1);
5517 _drag_rect->set_x1 (x2);
5520 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5522 , _cumulative_dx (0)
5523 , _cumulative_dy (0)
5524 , _was_selected (false)
5526 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5528 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5530 _region = &_primary->region_view ();
5531 _note_height = _region->midi_stream_view()->note_height ();
5535 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5537 Drag::start_grab (event);
5538 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5540 if (!(_was_selected = _primary->selected())) {
5542 /* tertiary-click means extend selection - we'll do that on button release,
5543 so don't add it here, because otherwise we make it hard to figure
5544 out the "extend-to" range.
5547 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5550 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5553 _region->note_selected (_primary, true);
5555 _editor->get_selection().clear_points();
5556 _region->unique_select (_primary);
5562 /** @return Current total drag x change in frames */
5564 NoteDrag::total_dx (const guint state) const
5567 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5569 /* primary note time */
5570 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5572 /* new time of the primary note in session frames */
5573 frameoffset_t st = n + dx + snap_delta (state);
5575 framepos_t const rp = _region->region()->position ();
5577 /* prevent the note being dragged earlier than the region's position */
5580 /* possibly snap and return corresponding delta */
5584 if (ArdourKeyboard::indicates_snap (state)) {
5585 if (_editor->snap_mode () != SnapOff) {
5589 if (_editor->snap_mode () == SnapOff) {
5591 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5592 if (ArdourKeyboard::indicates_snap_delta (state)) {
5600 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5601 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5603 ret = st - n - snap_delta (state);
5608 /** @return Current total drag y change in note number */
5610 NoteDrag::total_dy () const
5612 MidiStreamView* msv = _region->midi_stream_view ();
5613 double const y = _region->midi_view()->y_position ();
5614 /* new current note */
5615 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5617 n = max (msv->lowest_note(), n);
5618 n = min (msv->highest_note(), n);
5619 /* and work out delta */
5620 return n - msv->y_to_note (grab_y() - y);
5624 NoteDrag::motion (GdkEvent * event, bool)
5626 /* Total change in x and y since the start of the drag */
5627 frameoffset_t const dx = total_dx (event->button.state);
5628 int8_t const dy = total_dy ();
5630 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5631 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5632 double const tdy = -dy * _note_height - _cumulative_dy;
5635 _cumulative_dx += tdx;
5636 _cumulative_dy += tdy;
5638 int8_t note_delta = total_dy();
5640 _region->move_selection (tdx, tdy, note_delta);
5642 /* the new note value may be the same as the old one, but we
5643 * don't know what that means because the selection may have
5644 * involved more than one note and we might be doing something
5645 * odd with them. so show the note value anyway, always.
5648 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5650 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5655 NoteDrag::finished (GdkEvent* ev, bool moved)
5658 /* no motion - select note */
5660 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5661 _editor->current_mouse_mode() == Editing::MouseDraw) {
5663 bool changed = false;
5665 if (_was_selected) {
5666 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5668 _region->note_deselected (_primary);
5671 _editor->get_selection().clear_points();
5672 _region->unique_select (_primary);
5676 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5677 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5679 if (!extend && !add && _region->selection_size() > 1) {
5680 _editor->get_selection().clear_points();
5681 _region->unique_select (_primary);
5683 } else if (extend) {
5684 _region->note_selected (_primary, true, true);
5687 /* it was added during button press */
5694 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5695 _editor->commit_reversible_selection_op();
5699 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5704 NoteDrag::aborted (bool)
5709 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5710 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5711 : Drag (editor, atv->base_item ())
5713 , _y_origin (atv->y_position())
5714 , _nothing_to_drag (false)
5716 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5717 setup (atv->lines ());
5720 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5721 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5722 : Drag (editor, rv->get_canvas_group ())
5724 , _y_origin (rv->get_time_axis_view().y_position())
5725 , _nothing_to_drag (false)
5728 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5730 list<boost::shared_ptr<AutomationLine> > lines;
5732 AudioRegionView* audio_view;
5733 AutomationRegionView* automation_view;
5734 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5735 lines.push_back (audio_view->get_gain_line ());
5736 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5737 lines.push_back (automation_view->line ());
5740 error << _("Automation range drag created for invalid region type") << endmsg;
5746 /** @param lines AutomationLines to drag.
5747 * @param offset Offset from the session start to the points in the AutomationLines.
5750 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5752 /* find the lines that overlap the ranges being dragged */
5753 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5754 while (i != lines.end ()) {
5755 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5758 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5760 /* check this range against all the AudioRanges that we are using */
5761 list<AudioRange>::const_iterator k = _ranges.begin ();
5762 while (k != _ranges.end()) {
5763 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5769 /* add it to our list if it overlaps at all */
5770 if (k != _ranges.end()) {
5775 _lines.push_back (n);
5781 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5785 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5787 return 1.0 - ((global_y - _y_origin) / line->height());
5791 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5793 const double v = list->eval(x);
5794 return _integral ? rint(v) : v;
5798 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5800 Drag::start_grab (event, cursor);
5802 /* Get line states before we start changing things */
5803 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5804 i->state = &i->line->get_state ();
5805 i->original_fraction = y_fraction (i->line, current_pointer_y());
5808 if (_ranges.empty()) {
5810 /* No selected time ranges: drag all points */
5811 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5812 uint32_t const N = i->line->npoints ();
5813 for (uint32_t j = 0; j < N; ++j) {
5814 i->points.push_back (i->line->nth (j));
5820 if (_nothing_to_drag) {
5826 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5828 if (_nothing_to_drag && !first_move) {
5833 _editor->begin_reversible_command (_("automation range move"));
5835 if (!_ranges.empty()) {
5837 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5839 framecnt_t const half = (i->start + i->end) / 2;
5841 /* find the line that this audio range starts in */
5842 list<Line>::iterator j = _lines.begin();
5843 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5847 if (j != _lines.end()) {
5848 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5850 /* j is the line that this audio range starts in; fade into it;
5851 64 samples length plucked out of thin air.
5854 framepos_t a = i->start + 64;
5859 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5860 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5862 XMLNode &before = the_list->get_state();
5863 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5864 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5866 if (add_p || add_q) {
5867 _editor->session()->add_command (
5868 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5872 /* same thing for the end */
5875 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5879 if (j != _lines.end()) {
5880 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5882 /* j is the line that this audio range starts in; fade out of it;
5883 64 samples length plucked out of thin air.
5886 framepos_t b = i->end - 64;
5891 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5892 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5894 XMLNode &before = the_list->get_state();
5895 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5896 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5898 if (add_p || add_q) {
5899 _editor->session()->add_command (
5900 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5905 _nothing_to_drag = true;
5907 /* Find all the points that should be dragged and put them in the relevant
5908 points lists in the Line structs.
5911 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5913 uint32_t const N = i->line->npoints ();
5914 for (uint32_t j = 0; j < N; ++j) {
5916 /* here's a control point on this line */
5917 ControlPoint* p = i->line->nth (j);
5918 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5920 /* see if it's inside a range */
5921 list<AudioRange>::const_iterator k = _ranges.begin ();
5922 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5926 if (k != _ranges.end()) {
5927 /* dragging this point */
5928 _nothing_to_drag = false;
5929 i->points.push_back (p);
5935 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5936 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5940 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5941 float const f = y_fraction (l->line, current_pointer_y());
5942 /* we are ignoring x position for this drag, so we can just pass in anything */
5943 pair<double, float> result;
5945 result = l->line->drag_motion (0, f, true, false, ignored);
5946 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5951 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5953 if (_nothing_to_drag || !motion_occurred) {
5957 motion (event, false);
5958 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5959 i->line->end_drag (false, 0);
5962 _editor->commit_reversible_command ();
5966 AutomationRangeDrag::aborted (bool)
5968 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5973 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5975 , initial_time_axis_view (itav)
5977 /* note that time_axis_view may be null if the regionview was created
5978 * as part of a copy operation.
5980 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5981 layer = v->region()->layer ();
5982 initial_y = v->get_canvas_group()->position().y;
5983 initial_playlist = v->region()->playlist ();
5984 initial_position = v->region()->position ();
5985 initial_end = v->region()->position () + v->region()->length ();
5988 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5989 : Drag (e, i->canvas_item ())
5992 , _cumulative_dx (0)
5994 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5995 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6000 PatchChangeDrag::motion (GdkEvent* ev, bool)
6002 framepos_t f = adjusted_current_frame (ev);
6003 boost::shared_ptr<Region> r = _region_view->region ();
6004 f = max (f, r->position ());
6005 f = min (f, r->last_frame ());
6007 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6008 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6009 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6010 _cumulative_dx = dxu;
6014 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6016 if (!movement_occurred) {
6017 if (was_double_click()) {
6018 _region_view->edit_patch_change (_patch_change);
6023 boost::shared_ptr<Region> r (_region_view->region ());
6024 framepos_t f = adjusted_current_frame (ev);
6025 f = max (f, r->position ());
6026 f = min (f, r->last_frame ());
6028 _region_view->move_patch_change (
6030 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6035 PatchChangeDrag::aborted (bool)
6037 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6041 PatchChangeDrag::setup_pointer_frame_offset ()
6043 boost::shared_ptr<Region> region = _region_view->region ();
6044 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6047 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6048 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6055 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6057 _region_view->update_drag_selection (
6059 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6063 MidiRubberbandSelectDrag::deselect_things ()
6068 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6069 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6072 _vertical_only = true;
6076 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6078 double const y = _region_view->midi_view()->y_position ();
6080 y1 = max (0.0, y1 - y);
6081 y2 = max (0.0, y2 - y);
6083 _region_view->update_vertical_drag_selection (
6086 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6091 MidiVerticalSelectDrag::deselect_things ()
6096 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6097 : RubberbandSelectDrag (e, i)
6103 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6105 if (drag_in_progress) {
6106 /* We just want to select things at the end of the drag, not during it */
6110 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6112 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6114 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6116 _editor->commit_reversible_selection_op ();
6120 EditorRubberbandSelectDrag::deselect_things ()
6122 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6124 _editor->selection->clear_tracks();
6125 _editor->selection->clear_regions();
6126 _editor->selection->clear_points ();
6127 _editor->selection->clear_lines ();
6128 _editor->selection->clear_midi_notes ();
6130 _editor->commit_reversible_selection_op();
6133 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6138 _note[0] = _note[1] = 0;
6141 NoteCreateDrag::~NoteCreateDrag ()
6147 NoteCreateDrag::grid_frames (framepos_t t) const
6150 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6152 grid_beats = Evoral::Beats(1);
6155 return _region_view->region_beats_to_region_frames (grid_beats);
6159 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6161 Drag::start_grab (event, cursor);
6163 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6165 framepos_t pf = _drags->current_pointer_frame ();
6166 framecnt_t const g = grid_frames (pf);
6168 /* Hack so that we always snap to the note that we are over, instead of snapping
6169 to the next one if we're more than halfway through the one we're over.
6171 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6175 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6176 _note[1] = _note[0];
6178 MidiStreamView* sv = _region_view->midi_stream_view ();
6179 double const x = _editor->sample_to_pixel (_note[0]);
6180 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6182 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6183 _drag_rect->set_outline_all ();
6184 _drag_rect->set_outline_color (0xffffff99);
6185 _drag_rect->set_fill_color (0xffffff66);
6189 NoteCreateDrag::motion (GdkEvent* event, bool)
6191 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6192 double const x0 = _editor->sample_to_pixel (_note[0]);
6193 double const x1 = _editor->sample_to_pixel (_note[1]);
6194 _drag_rect->set_x0 (std::min(x0, x1));
6195 _drag_rect->set_x1 (std::max(x0, x1));
6199 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6201 if (!had_movement) {
6205 framepos_t const start = min (_note[0], _note[1]);
6206 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6208 framecnt_t const g = grid_frames (start);
6209 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6211 if (_editor->snap_mode() == SnapNormal && length < g) {
6215 Evoral::Beats length_beats = max (
6216 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6218 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6222 NoteCreateDrag::y_to_region (double y) const
6225 _region_view->get_canvas_group()->canvas_to_item (x, y);
6230 NoteCreateDrag::aborted (bool)
6235 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6240 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6244 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6246 Drag::start_grab (event, cursor);
6250 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6256 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6259 distance = _drags->current_pointer_x() - grab_x();
6260 len = ar->fade_in()->back()->when;
6262 distance = grab_x() - _drags->current_pointer_x();
6263 len = ar->fade_out()->back()->when;
6266 /* how long should it be ? */
6268 new_length = len + _editor->pixel_to_sample (distance);
6270 /* now check with the region that this is legal */
6272 new_length = ar->verify_xfade_bounds (new_length, start);
6275 arv->reset_fade_in_shape_width (ar, new_length);
6277 arv->reset_fade_out_shape_width (ar, new_length);
6282 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6288 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6291 distance = _drags->current_pointer_x() - grab_x();
6292 len = ar->fade_in()->back()->when;
6294 distance = grab_x() - _drags->current_pointer_x();
6295 len = ar->fade_out()->back()->when;
6298 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6300 _editor->begin_reversible_command ("xfade trim");
6301 ar->playlist()->clear_owned_changes ();
6304 ar->set_fade_in_length (new_length);
6306 ar->set_fade_out_length (new_length);
6309 /* Adjusting the xfade may affect other regions in the playlist, so we need
6310 to get undo Commands from the whole playlist rather than just the
6314 vector<Command*> cmds;
6315 ar->playlist()->rdiff (cmds);
6316 _editor->session()->add_commands (cmds);
6317 _editor->commit_reversible_command ();
6322 CrossfadeEdgeDrag::aborted (bool)
6325 // arv->redraw_start_xfade ();
6327 // arv->redraw_end_xfade ();
6331 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6332 : Drag (e, item, true)
6333 , line (new EditorCursor (*e))
6335 line->set_position (pos);
6339 RegionCutDrag::~RegionCutDrag ()
6345 RegionCutDrag::motion (GdkEvent*, bool)
6347 framepos_t where = _drags->current_pointer_frame();
6348 _editor->snap_to (where);
6350 line->set_position (where);
6354 RegionCutDrag::finished (GdkEvent* event, bool)
6356 _editor->get_track_canvas()->canvas()->re_enter();
6358 framepos_t pos = _drags->current_pointer_frame();
6362 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6368 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state));
6372 RegionCutDrag::aborted (bool)