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)
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);
525 return boost::shared_ptr<Region>();
528 struct PresentationInfoTimeAxisViewSorter {
529 bool operator() (TimeAxisView* a, TimeAxisView* b) {
530 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
531 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
533 return ra->route()->presentation_info () < rb->route()->presentation_info();
537 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
542 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
544 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
545 as some of the regions we are dragging may be on such tracks.
548 TrackViewList track_views = _editor->track_views;
549 track_views.sort (PresentationInfoTimeAxisViewSorter ());
551 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
552 _time_axis_views.push_back (*i);
554 TimeAxisView::Children children_list = (*i)->get_child_list ();
555 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
556 _time_axis_views.push_back (j->get());
560 /* the list of views can be empty at this point if this is a region list-insert drag
563 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
564 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
567 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
571 RegionDrag::region_going_away (RegionView* v)
573 list<DraggingView>::iterator i = _views.begin ();
574 while (i != _views.end() && i->view != v) {
578 if (i != _views.end()) {
583 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
584 * or -1 if it is not found.
587 RegionDrag::find_time_axis_view (TimeAxisView* t) const
590 int const N = _time_axis_views.size ();
591 while (i < N && _time_axis_views[i] != t) {
595 if (_time_axis_views[i] != t) {
602 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
603 : RegionDrag (e, i, p, v)
605 , _ignore_video_lock (false)
607 , _last_pointer_time_axis_view (0)
608 , _last_pointer_layer (0)
613 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
617 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
619 Drag::start_grab (event, cursor);
620 setup_snap_delta (_last_frame_position);
622 show_verbose_cursor_time (_last_frame_position);
624 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
626 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
627 assert(_last_pointer_time_axis_view >= 0);
628 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
631 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
632 _ignore_video_lock = true;
636 /* cross track dragging seems broken here. disabled for now. */
637 _y_constrained = true;
642 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
644 /* compute the amount of pointer motion in frames, and where
645 the region would be if we moved it by that much.
647 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
649 framepos_t sync_frame;
650 framecnt_t sync_offset;
653 sync_offset = _primary->region()->sync_offset (sync_dir);
655 /* we don't handle a sync point that lies before zero.
657 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
659 framecnt_t const sd = snap_delta (event->button.state);
660 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
662 _editor->snap_to_with_modifier (sync_frame, event);
664 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
667 *pending_region_position = _last_frame_position;
670 if (*pending_region_position > max_framepos - _primary->region()->length()) {
671 *pending_region_position = _last_frame_position;
676 bool const x_move_allowed = !_x_constrained;
678 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
680 /* x movement since last time (in pixels) */
681 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
683 /* total x movement */
684 framecnt_t total_dx = *pending_region_position;
685 if (regions_came_from_canvas()) {
686 total_dx = total_dx - grab_frame ();
689 /* check that no regions have gone off the start of the session */
690 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
691 if ((i->view->region()->position() + total_dx) < 0) {
693 *pending_region_position = _last_frame_position;
704 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
710 const int tavsize = _time_axis_views.size();
711 const int dt = delta > 0 ? +1 : -1;
713 int target = start + delta - skip;
715 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
716 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
718 while (current >= 0 && current != target) {
720 if (current < 0 && dt < 0) {
723 if (current >= tavsize && dt > 0) {
726 if (current < 0 || current >= tavsize) {
730 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
731 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
735 if (distance_only && current == start + delta) {
743 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
745 if (_y_constrained) {
749 const int tavsize = _time_axis_views.size();
750 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
751 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
752 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
754 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
755 /* already in the drop zone */
756 if (delta_track >= 0) {
757 /* downward motion - OK if others are still not in the dropzone */
766 } else if (n >= tavsize) {
767 /* downward motion into drop zone. That's fine. */
771 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
772 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
773 /* not a track, or the wrong type */
777 double const l = i->layer + delta_layer;
779 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
780 mode to allow the user to place a region below another on layer 0.
782 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
783 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
784 If it has, the layers will be munged later anyway, so it's ok.
790 /* all regions being dragged are ok with this change */
794 struct DraggingViewSorter {
795 bool operator() (const DraggingView& a, const DraggingView& b) {
796 return a.time_axis_view < b.time_axis_view;
801 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
803 double delta_layer = 0;
804 int delta_time_axis_view = 0;
805 int current_pointer_time_axis_view = -1;
807 assert (!_views.empty ());
809 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
811 /* Find the TimeAxisView that the pointer is now over */
812 const double cur_y = current_pointer_y ();
813 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
814 TimeAxisView* tv = r.first;
816 if (!tv && cur_y < 0) {
817 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
821 /* find drop-zone y-position */
822 Coord last_track_bottom_edge;
823 last_track_bottom_edge = 0;
824 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
825 if (!(*t)->hidden()) {
826 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
831 if (tv && tv->view()) {
832 /* the mouse is over a track */
833 double layer = r.second;
835 if (first_move && tv->view()->layer_display() == Stacked) {
836 tv->view()->set_layer_display (Expanded);
839 /* Here's the current pointer position in terms of time axis view and layer */
840 current_pointer_time_axis_view = find_time_axis_view (tv);
841 assert(current_pointer_time_axis_view >= 0);
843 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
845 /* Work out the change in y */
847 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
848 if (!rtv || !rtv->is_track()) {
849 /* ignore busses early on. we can't move any regions on them */
850 } else if (_last_pointer_time_axis_view < 0) {
851 /* Was in the drop-zone, now over a track.
852 * Hence it must be an upward move (from the bottom)
854 * track_index is still -1, so delta must be set to
855 * move up the correct number of tracks from the bottom.
857 * This is necessary because steps may be skipped if
858 * the bottom-most track is not a valid target and/or
859 * if there are hidden tracks at the bottom.
860 * Hence the initial offset (_ddropzone) as well as the
861 * last valid pointer position (_pdropzone) need to be
862 * taken into account.
864 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
866 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
869 /* TODO needs adjustment per DraggingView,
871 * e.g. select one region on the top-layer of a track
872 * and one region which is at the bottom-layer of another track
875 * Indicated drop-zones and layering is wrong.
876 * and may infer additional layers on the target-track
877 * (depending how many layers the original track had).
879 * Or select two regions (different layers) on a same track,
880 * move across a non-layer track.. -> layering info is lost.
881 * on drop either of the regions may be on top.
883 * Proposed solution: screw it :) well,
884 * don't use delta_layer, use an absolute value
885 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
886 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
887 * 3) iterate over all DraggingView, find the one that is over the track with most layers
888 * 4) proportionally scale layer to layers available on target
890 delta_layer = current_pointer_layer - _last_pointer_layer;
893 /* for automation lanes, there is a TimeAxisView but no ->view()
894 * if (!tv) -> dropzone
896 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
897 /* Moving into the drop-zone.. */
898 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
899 /* delta_time_axis_view may not be sufficient to move into the DZ
900 * the mouse may enter it, but it may not be a valid move due to
903 * -> remember the delta needed to move into the dropzone
905 _ddropzone = delta_time_axis_view;
906 /* ..but subtract hidden tracks (or routes) at the bottom.
907 * we silently move mover them
909 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
910 - _time_axis_views.size();
912 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
913 /* move around inside the zone.
914 * This allows to move further down until all regions are in the zone.
916 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
917 assert(ptr_y >= last_track_bottom_edge);
918 assert(_ddropzone > 0);
920 /* calculate mouse position in 'tracks' below last track. */
921 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
922 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
924 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
926 delta_time_axis_view = dzpos - _pdropzone;
927 } else if (dzpos < _pdropzone && _ndropzone > 0) {
928 // move up inside the DZ
929 delta_time_axis_view = dzpos - _pdropzone;
933 /* Work out the change in x */
934 framepos_t pending_region_position;
935 double const x_delta = compute_x_delta (event, &pending_region_position);
936 _last_frame_position = pending_region_position;
938 /* calculate hidden tracks in current y-axis delta */
940 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
941 /* The mouse is more than one track below the dropzone.
942 * distance calculation is not needed (and would not work, either
943 * because the dropzone is "packed").
945 * Except when [partially] moving regions out of dropzone in a large step.
946 * (the mouse may or may not remain in the DZ)
947 * Hidden tracks at the bottom of the TAV need to be skipped.
949 * This also handles the case if the mouse entered the DZ
950 * in a large step (exessive delta), either due to fast-movement,
951 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
953 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
954 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
956 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
957 -_time_axis_views.size() - dt;
960 else if (_last_pointer_time_axis_view < 0) {
961 /* Moving out of the zone. Check for hidden tracks at the bottom. */
962 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
963 -_time_axis_views.size() - delta_time_axis_view;
965 /* calculate hidden tracks that are skipped by the pointer movement */
966 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
967 - _last_pointer_time_axis_view
968 - delta_time_axis_view;
971 /* Verify change in y */
972 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
973 /* this y movement is not allowed, so do no y movement this time */
974 delta_time_axis_view = 0;
979 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
980 /* haven't reached next snap point, and we're not switching
981 trackviews nor layers. nothing to do.
986 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
987 PlaylistDropzoneMap playlist_dropzone_map;
988 _ndropzone = 0; // number of elements currently in the dropzone
991 /* sort views by time_axis.
992 * This retains track order in the dropzone, regardless
993 * of actual selection order
995 _views.sort (DraggingViewSorter());
997 /* count number of distinct tracks of all regions
998 * being dragged, used for dropzone.
1000 int prev_track = -1;
1001 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1002 if (i->time_axis_view != prev_track) {
1003 prev_track = i->time_axis_view;
1009 _views.back().time_axis_view -
1010 _views.front().time_axis_view;
1012 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1013 - _views.back().time_axis_view;
1015 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1019 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1021 RegionView* rv = i->view;
1026 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1033 /* reparent the regionview into a group above all
1037 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1038 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1039 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1040 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1041 /* move the item so that it continues to appear at the
1042 same location now that its parent has changed.
1044 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1047 /* If we have moved tracks, we'll fudge the layer delta so that the
1048 region gets moved back onto layer 0 on its new track; this avoids
1049 confusion when dragging regions from non-zero layers onto different
1052 double this_delta_layer = delta_layer;
1053 if (delta_time_axis_view != 0) {
1054 this_delta_layer = - i->layer;
1057 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1059 int track_index = i->time_axis_view + this_delta_time_axis_view;
1060 assert(track_index >= 0);
1062 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1063 /* Track is in the Dropzone */
1065 i->time_axis_view = track_index;
1066 assert(i->time_axis_view >= (int) _time_axis_views.size());
1069 double yposition = 0;
1070 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1071 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1074 /* store index of each new playlist as a negative count, starting at -1 */
1076 if (pdz == playlist_dropzone_map.end()) {
1077 /* compute where this new track (which doesn't exist yet) will live
1080 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1082 /* How high is this region view ? */
1084 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1085 ArdourCanvas::Rect bbox;
1088 bbox = obbox.get ();
1091 last_track_bottom_edge += bbox.height();
1093 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1096 yposition = pdz->second;
1099 /* values are zero or negative, hence the use of min() */
1100 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1105 /* The TimeAxisView that this region is now over */
1106 TimeAxisView* current_tv = _time_axis_views[track_index];
1108 /* Ensure it is moved from stacked -> expanded if appropriate */
1109 if (current_tv->view()->layer_display() == Stacked) {
1110 current_tv->view()->set_layer_display (Expanded);
1113 /* We're only allowed to go -ve in layer on Expanded views */
1114 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1115 this_delta_layer = - i->layer;
1119 rv->set_height (current_tv->view()->child_height ());
1121 /* Update show/hidden status as the region view may have come from a hidden track,
1122 or have moved to one.
1124 if (current_tv->hidden ()) {
1125 rv->get_canvas_group()->hide ();
1127 rv->get_canvas_group()->show ();
1130 /* Update the DraggingView */
1131 i->time_axis_view = track_index;
1132 i->layer += this_delta_layer;
1135 _editor->mouse_brush_insert_region (rv, pending_region_position);
1139 /* Get the y coordinate of the top of the track that this region is now over */
1140 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1142 /* And adjust for the layer that it should be on */
1143 StreamView* cv = current_tv->view ();
1144 switch (cv->layer_display ()) {
1148 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1151 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1155 /* need to get the parent of the regionview
1156 * canvas group and get its position in
1157 * equivalent coordinate space as the trackview
1158 * we are now dragging over.
1161 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1166 /* Now move the region view */
1167 rv->move (x_delta, y_delta);
1169 } /* foreach region */
1171 _total_x_delta += x_delta;
1173 if (x_delta != 0 && !_brushing) {
1174 show_verbose_cursor_time (_last_frame_position);
1177 /* keep track of pointer movement */
1179 /* the pointer is currently over a time axis view */
1181 if (_last_pointer_time_axis_view < 0) {
1182 /* last motion event was not over a time axis view
1183 * or last y-movement out of the dropzone was not valid
1186 if (delta_time_axis_view < 0) {
1187 /* in the drop zone, moving up */
1189 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1190 * We do not use negative _last_pointer_time_axis_view because
1191 * the dropzone is "packed" (the actual track offset is ignored)
1193 * As opposed to the actual number
1194 * of elements in the dropzone (_ndropzone)
1195 * _pdropzone is not constrained. This is necessary
1196 * to allow moving multiple regions with y-distance
1199 * There can be 0 elements in the dropzone,
1200 * even though the drag-pointer is inside the DZ.
1203 * [ Audio-track, Midi-track, Audio-track, DZ ]
1204 * move regions from both audio tracks at the same time into the
1205 * DZ by grabbing the region in the bottom track.
1207 assert(current_pointer_time_axis_view >= 0);
1208 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1212 /* only move out of the zone if the movement is OK */
1213 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1214 assert(delta_time_axis_view < 0);
1215 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1216 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1217 * the current position can be calculated as follows:
1219 // a well placed oofus attack can still throw this off.
1220 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1221 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1224 /* last motion event was also over a time axis view */
1225 _last_pointer_time_axis_view += delta_time_axis_view;
1226 assert(_last_pointer_time_axis_view >= 0);
1231 /* the pointer is not over a time axis view */
1232 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1233 _pdropzone += delta_time_axis_view - delta_skip;
1234 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1237 _last_pointer_layer += delta_layer;
1241 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1243 if (_copy && first_move) {
1244 if (_x_constrained && !_brushing) {
1245 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1246 } else if (!_brushing) {
1247 _editor->begin_reversible_command (Operations::region_copy);
1248 } else if (_brushing) {
1249 _editor->begin_reversible_command (Operations::drag_region_brush);
1251 /* duplicate the regionview(s) and region(s) */
1253 list<DraggingView> new_regionviews;
1255 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1257 RegionView* rv = i->view;
1258 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1259 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1261 const boost::shared_ptr<const Region> original = rv->region();
1262 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1263 region_copy->set_position (original->position());
1264 /* need to set this so that the drop zone code can work. This doesn't
1265 actually put the region into the playlist, but just sets a weak pointer
1268 region_copy->set_playlist (original->playlist());
1272 boost::shared_ptr<AudioRegion> audioregion_copy
1273 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1275 nrv = new AudioRegionView (*arv, audioregion_copy);
1277 boost::shared_ptr<MidiRegion> midiregion_copy
1278 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1279 nrv = new MidiRegionView (*mrv, midiregion_copy);
1284 nrv->get_canvas_group()->show ();
1285 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1287 /* swap _primary to the copy */
1289 if (rv == _primary) {
1293 /* ..and deselect the one we copied */
1295 rv->set_selected (false);
1298 if (!new_regionviews.empty()) {
1300 /* reflect the fact that we are dragging the copies */
1302 _views = new_regionviews;
1304 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1307 } else if (!_copy && first_move) {
1308 if (_x_constrained && !_brushing) {
1309 _editor->begin_reversible_command (_("fixed time region drag"));
1310 } else if (!_brushing) {
1311 _editor->begin_reversible_command (Operations::region_drag);
1312 } else if (_brushing) {
1313 _editor->begin_reversible_command (Operations::drag_region_brush);
1316 RegionMotionDrag::motion (event, first_move);
1320 RegionMotionDrag::finished (GdkEvent *, bool)
1322 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1323 if (!(*i)->view()) {
1327 if ((*i)->view()->layer_display() == Expanded) {
1328 (*i)->view()->set_layer_display (Stacked);
1334 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1336 RegionMotionDrag::finished (ev, movement_occurred);
1338 if (!movement_occurred) {
1342 if (was_double_click() && !_views.empty()) {
1343 DraggingView dv = _views.front();
1344 dv.view->show_region_editor ();
1351 assert (!_views.empty ());
1353 /* We might have hidden region views so that they weren't visible during the drag
1354 (when they have been reparented). Now everything can be shown again, as region
1355 views are back in their track parent groups.
1357 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1358 i->view->get_canvas_group()->show ();
1361 bool const changed_position = (_last_frame_position != _primary->region()->position());
1362 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1363 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1383 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1387 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1389 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1394 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1395 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1396 uint32_t output_chan = region->n_channels();
1397 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1398 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1400 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1401 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1403 rtav->set_height (original->current_height());
1407 ChanCount one_midi_port (DataType::MIDI, 1);
1408 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1409 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1410 (ARDOUR::Plugin::PresetRecord*) 0,
1411 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1412 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1414 rtav->set_height (original->current_height());
1419 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1425 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1427 RegionSelection new_views;
1428 PlaylistSet modified_playlists;
1429 RouteTimeAxisView* new_time_axis_view = 0;
1432 /* all changes were made during motion event handlers */
1434 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1438 _editor->commit_reversible_command ();
1442 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1443 PlaylistMapping playlist_mapping;
1445 /* insert the regions into their new playlists */
1446 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1448 RouteTimeAxisView* dest_rtv = 0;
1450 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1456 if (changed_position && !_x_constrained) {
1457 where = i->view->region()->position() - drag_delta;
1459 where = i->view->region()->position();
1462 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1463 /* dragged to drop zone */
1465 PlaylistMapping::iterator pm;
1467 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1468 /* first region from this original playlist: create a new track */
1469 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1470 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1471 dest_rtv = new_time_axis_view;
1473 /* we already created a new track for regions from this playlist, use it */
1474 dest_rtv = pm->second;
1477 /* destination time axis view is the one we dragged to */
1478 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1481 if (dest_rtv != 0) {
1482 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1483 if (new_view != 0) {
1484 new_views.push_back (new_view);
1488 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1489 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1492 list<DraggingView>::const_iterator next = i;
1498 /* If we've created new regions either by copying or moving
1499 to a new track, we want to replace the old selection with the new ones
1502 if (new_views.size() > 0) {
1503 _editor->selection->set (new_views);
1506 /* write commands for the accumulated diffs for all our modified playlists */
1507 add_stateful_diff_commands_for_playlists (modified_playlists);
1509 _editor->commit_reversible_command ();
1513 RegionMoveDrag::finished_no_copy (
1514 bool const changed_position,
1515 bool const changed_tracks,
1516 framecnt_t const drag_delta
1519 RegionSelection new_views;
1520 PlaylistSet modified_playlists;
1521 PlaylistSet frozen_playlists;
1522 set<RouteTimeAxisView*> views_to_update;
1523 RouteTimeAxisView* new_time_axis_view = 0;
1525 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1526 PlaylistMapping playlist_mapping;
1528 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1530 RegionView* rv = i->view;
1531 RouteTimeAxisView* dest_rtv = 0;
1533 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1538 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1539 /* dragged to drop zone */
1541 PlaylistMapping::iterator pm;
1543 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1544 /* first region from this original playlist: create a new track */
1545 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1546 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1547 dest_rtv = new_time_axis_view;
1549 /* we already created a new track for regions from this playlist, use it */
1550 dest_rtv = pm->second;
1554 /* destination time axis view is the one we dragged to */
1555 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1560 double const dest_layer = i->layer;
1562 views_to_update.insert (dest_rtv);
1566 if (changed_position && !_x_constrained) {
1567 where = rv->region()->position() - drag_delta;
1569 where = rv->region()->position();
1572 if (changed_tracks) {
1574 /* insert into new playlist */
1576 RegionView* new_view = insert_region_into_playlist (
1577 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1580 if (new_view == 0) {
1585 new_views.push_back (new_view);
1587 /* remove from old playlist */
1589 /* the region that used to be in the old playlist is not
1590 moved to the new one - we use a copy of it. as a result,
1591 any existing editor for the region should no longer be
1594 rv->hide_region_editor();
1597 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1601 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1603 /* this movement may result in a crossfade being modified, or a layering change,
1604 so we need to get undo data from the playlist as well as the region.
1607 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1609 playlist->clear_changes ();
1612 rv->region()->clear_changes ();
1615 motion on the same track. plonk the previously reparented region
1616 back to its original canvas group (its streamview).
1617 No need to do anything for copies as they are fake regions which will be deleted.
1620 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1621 rv->get_canvas_group()->set_y_position (i->initial_y);
1624 /* just change the model */
1625 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1626 playlist->set_layer (rv->region(), dest_layer);
1629 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1631 r = frozen_playlists.insert (playlist);
1634 playlist->freeze ();
1637 rv->region()->set_position (where);
1638 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1641 if (changed_tracks) {
1643 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1644 was selected in all of them, then removing it from a playlist will have removed all
1645 trace of it from _views (i.e. there were N regions selected, we removed 1,
1646 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1647 corresponding regionview, and _views is now empty).
1649 This could have invalidated any and all iterators into _views.
1651 The heuristic we use here is: if the region selection is empty, break out of the loop
1652 here. if the region selection is not empty, then restart the loop because we know that
1653 we must have removed at least the region(view) we've just been working on as well as any
1654 that we processed on previous iterations.
1656 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1657 we can just iterate.
1661 if (_views.empty()) {
1672 /* If we've created new regions either by copying or moving
1673 to a new track, we want to replace the old selection with the new ones
1676 if (new_views.size() > 0) {
1677 _editor->selection->set (new_views);
1680 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1684 /* write commands for the accumulated diffs for all our modified playlists */
1685 add_stateful_diff_commands_for_playlists (modified_playlists);
1686 /* applies to _brushing */
1687 _editor->commit_reversible_command ();
1689 /* We have futzed with the layering of canvas items on our streamviews.
1690 If any region changed layer, this will have resulted in the stream
1691 views being asked to set up their region views, and all will be well.
1692 If not, we might now have badly-ordered region views. Ask the StreamViews
1693 involved to sort themselves out, just in case.
1696 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1697 (*i)->view()->playlist_layered ((*i)->track ());
1701 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1702 * @param region Region to remove.
1703 * @param playlist playlist To remove from.
1704 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1705 * that clear_changes () is only called once per playlist.
1708 RegionMoveDrag::remove_region_from_playlist (
1709 boost::shared_ptr<Region> region,
1710 boost::shared_ptr<Playlist> playlist,
1711 PlaylistSet& modified_playlists
1714 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1717 playlist->clear_changes ();
1720 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1724 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1725 * clearing the playlist's diff history first if necessary.
1726 * @param region Region to insert.
1727 * @param dest_rtv Destination RouteTimeAxisView.
1728 * @param dest_layer Destination layer.
1729 * @param where Destination position.
1730 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1731 * that clear_changes () is only called once per playlist.
1732 * @return New RegionView, or 0 if no insert was performed.
1735 RegionMoveDrag::insert_region_into_playlist (
1736 boost::shared_ptr<Region> region,
1737 RouteTimeAxisView* dest_rtv,
1740 PlaylistSet& modified_playlists
1743 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1744 if (!dest_playlist) {
1748 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1749 _new_region_view = 0;
1750 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1752 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1753 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1755 dest_playlist->clear_changes ();
1758 dest_playlist->add_region (region, where);
1760 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1761 dest_playlist->set_layer (region, dest_layer);
1766 assert (_new_region_view);
1768 return _new_region_view;
1772 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1774 _new_region_view = rv;
1778 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1780 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1781 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1783 _editor->session()->add_command (c);
1792 RegionMoveDrag::aborted (bool movement_occurred)
1796 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1797 list<DraggingView>::const_iterator next = i;
1806 RegionMotionDrag::aborted (movement_occurred);
1811 RegionMotionDrag::aborted (bool)
1813 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1815 StreamView* sview = (*i)->view();
1818 if (sview->layer_display() == Expanded) {
1819 sview->set_layer_display (Stacked);
1824 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1825 RegionView* rv = i->view;
1826 TimeAxisView* tv = &(rv->get_time_axis_view ());
1827 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1829 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1830 rv->get_canvas_group()->set_y_position (0);
1832 rv->move (-_total_x_delta, 0);
1833 rv->set_height (rtv->view()->child_height ());
1837 /** @param b true to brush, otherwise false.
1838 * @param c true to make copies of the regions being moved, otherwise false.
1840 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1841 : RegionMotionDrag (e, i, p, v, b)
1843 , _new_region_view (0)
1845 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1848 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1849 if (rtv && rtv->is_track()) {
1850 speed = rtv->track()->speed ();
1853 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1857 RegionMoveDrag::setup_pointer_frame_offset ()
1859 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1862 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1863 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1865 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1867 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1868 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1870 _primary = v->view()->create_region_view (r, false, false);
1872 _primary->get_canvas_group()->show ();
1873 _primary->set_position (pos, 0);
1874 _views.push_back (DraggingView (_primary, this, v));
1876 _last_frame_position = pos;
1878 _item = _primary->get_canvas_group ();
1882 RegionInsertDrag::finished (GdkEvent *, bool)
1884 int pos = _views.front().time_axis_view;
1885 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1887 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1889 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1890 _primary->get_canvas_group()->set_y_position (0);
1892 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1894 _editor->begin_reversible_command (Operations::insert_region);
1895 playlist->clear_changes ();
1896 playlist->add_region (_primary->region (), _last_frame_position);
1898 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1899 if (Config->get_edit_mode() == Ripple) {
1900 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1903 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1904 _editor->commit_reversible_command ();
1912 RegionInsertDrag::aborted (bool)
1919 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1920 : RegionMoveDrag (e, i, p, v, false, false)
1922 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1925 struct RegionSelectionByPosition {
1926 bool operator() (RegionView*a, RegionView* b) {
1927 return a->region()->position () < b->region()->position();
1932 RegionSpliceDrag::motion (GdkEvent* event, bool)
1934 /* Which trackview is this ? */
1936 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1937 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1939 /* The region motion is only processed if the pointer is over
1943 if (!tv || !tv->is_track()) {
1944 /* To make sure we hide the verbose canvas cursor when the mouse is
1945 not held over an audio track.
1947 _editor->verbose_cursor()->hide ();
1950 _editor->verbose_cursor()->show ();
1955 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1961 RegionSelection copy;
1962 _editor->selection->regions.by_position(copy);
1964 framepos_t const pf = adjusted_current_frame (event);
1966 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1968 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1974 boost::shared_ptr<Playlist> playlist;
1976 if ((playlist = atv->playlist()) == 0) {
1980 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1985 if (pf < (*i)->region()->last_frame() + 1) {
1989 if (pf > (*i)->region()->first_frame()) {
1995 playlist->shuffle ((*i)->region(), dir);
2000 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2002 RegionMoveDrag::finished (event, movement_occurred);
2006 RegionSpliceDrag::aborted (bool)
2016 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2019 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2021 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2022 RegionSelection to_ripple;
2023 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2024 if ((*i)->position() >= where) {
2025 to_ripple.push_back (rtv->view()->find_view(*i));
2029 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2030 if (!exclude.contains (*i)) {
2031 // the selection has already been added to _views
2033 if (drag_in_progress) {
2034 // do the same things that RegionMotionDrag::motion does when
2035 // first_move is true, for the region views that we're adding
2036 // to _views this time
2039 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2040 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2041 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2042 rvg->reparent (_editor->_drag_motion_group);
2044 // we only need to move in the y direction
2045 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2050 _views.push_back (DraggingView (*i, this, tav));
2056 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2059 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2060 // we added all the regions after the selection
2062 std::list<DraggingView>::iterator to_erase = i++;
2063 if (!_editor->selection->regions.contains (to_erase->view)) {
2064 // restore the non-selected regions to their original playlist & positions,
2065 // and then ripple them back by the length of the regions that were dragged away
2066 // do the same things as RegionMotionDrag::aborted
2068 RegionView *rv = to_erase->view;
2069 TimeAxisView* tv = &(rv->get_time_axis_view ());
2070 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2073 // plonk them back onto their own track
2074 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2075 rv->get_canvas_group()->set_y_position (0);
2079 // move the underlying region to match the view
2080 rv->region()->set_position (rv->region()->position() + amount);
2082 // restore the view to match the underlying region's original position
2083 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2086 rv->set_height (rtv->view()->child_height ());
2087 _views.erase (to_erase);
2093 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2095 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2097 return allow_moves_across_tracks;
2105 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2106 : RegionMoveDrag (e, i, p, v, false, false)
2108 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2109 // compute length of selection
2110 RegionSelection selected_regions = _editor->selection->regions;
2111 selection_length = selected_regions.end_frame() - selected_regions.start();
2113 // we'll only allow dragging to another track in ripple mode if all the regions
2114 // being dragged start off on the same track
2115 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2118 exclude = new RegionList;
2119 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2120 exclude->push_back((*i)->region());
2123 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2124 RegionSelection copy;
2125 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2127 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2128 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2130 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2131 // find ripple start point on each applicable playlist
2132 RegionView *first_selected_on_this_track = NULL;
2133 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2134 if ((*i)->region()->playlist() == (*pi)) {
2135 // region is on this playlist - it's the first, because they're sorted
2136 first_selected_on_this_track = *i;
2140 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2141 add_all_after_to_views (
2142 &first_selected_on_this_track->get_time_axis_view(),
2143 first_selected_on_this_track->region()->position(),
2144 selected_regions, false);
2147 if (allow_moves_across_tracks) {
2148 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2156 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2158 /* Which trackview is this ? */
2160 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2161 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2163 /* The region motion is only processed if the pointer is over
2167 if (!tv || !tv->is_track()) {
2168 /* To make sure we hide the verbose canvas cursor when the mouse is
2169 not held over an audiotrack.
2171 _editor->verbose_cursor()->hide ();
2175 framepos_t where = adjusted_current_frame (event);
2176 assert (where >= 0);
2178 double delta = compute_x_delta (event, &after);
2180 framecnt_t amount = _editor->pixel_to_sample (delta);
2182 if (allow_moves_across_tracks) {
2183 // all the originally selected regions were on the same track
2185 framecnt_t adjust = 0;
2186 if (prev_tav && tv != prev_tav) {
2187 // dragged onto a different track
2188 // remove the unselected regions from _views, restore them to their original positions
2189 // and add the regions after the drop point on the new playlist to _views instead.
2190 // undo the effect of rippling the previous playlist, and include the effect of removing
2191 // the dragged region(s) from this track
2193 remove_unselected_from_views (prev_amount, false);
2194 // ripple previous playlist according to the regions that have been removed onto the new playlist
2195 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2198 // move just the selected regions
2199 RegionMoveDrag::motion(event, first_move);
2201 // ensure that the ripple operation on the new playlist inserts selection_length time
2202 adjust = selection_length;
2203 // ripple the new current playlist
2204 tv->playlist()->ripple (where, amount+adjust, exclude);
2206 // add regions after point where drag entered this track to subsequent ripples
2207 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2210 // motion on same track
2211 RegionMoveDrag::motion(event, first_move);
2215 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2216 prev_position = where;
2218 // selection encompasses multiple tracks - just drag
2219 // cross-track drags are forbidden
2220 RegionMoveDrag::motion(event, first_move);
2223 if (!_x_constrained) {
2224 prev_amount += amount;
2227 _last_frame_position = after;
2231 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2233 if (!movement_occurred) {
2237 if (was_double_click() && !_views.empty()) {
2238 DraggingView dv = _views.front();
2239 dv.view->show_region_editor ();
2246 _editor->begin_reversible_command(_("Ripple drag"));
2248 // remove the regions being rippled from the dragging view, updating them to
2249 // their new positions
2250 remove_unselected_from_views (prev_amount, true);
2252 if (allow_moves_across_tracks) {
2254 // if regions were dragged across tracks, we've rippled any later
2255 // regions on the track the regions were dragged off, so we need
2256 // to add the original track to the undo record
2257 orig_tav->playlist()->clear_changes();
2258 vector<Command*> cmds;
2259 orig_tav->playlist()->rdiff (cmds);
2260 _editor->session()->add_commands (cmds);
2262 if (prev_tav && prev_tav != orig_tav) {
2263 prev_tav->playlist()->clear_changes();
2264 vector<Command*> cmds;
2265 prev_tav->playlist()->rdiff (cmds);
2266 _editor->session()->add_commands (cmds);
2269 // selection spanned multiple tracks - all will need adding to undo record
2271 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2272 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2274 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2275 (*pi)->clear_changes();
2276 vector<Command*> cmds;
2277 (*pi)->rdiff (cmds);
2278 _editor->session()->add_commands (cmds);
2282 // other modified playlists are added to undo by RegionMoveDrag::finished()
2283 RegionMoveDrag::finished (event, movement_occurred);
2284 _editor->commit_reversible_command();
2288 RegionRippleDrag::aborted (bool movement_occurred)
2290 RegionMoveDrag::aborted (movement_occurred);
2295 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2297 _view (dynamic_cast<MidiTimeAxisView*> (v))
2299 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2305 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2308 _editor->begin_reversible_command (_("create region"));
2309 _region = add_midi_region (_view, false);
2310 _view->playlist()->freeze ();
2313 framepos_t const f = adjusted_current_frame (event);
2314 if (f < grab_frame()) {
2315 _region->set_initial_position (f);
2318 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2319 so that if this region is duplicated, its duplicate starts on
2320 a snap point rather than 1 frame after a snap point. Otherwise things get
2321 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2322 place snapped notes at the start of the region.
2325 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2326 _region->set_length (len < 1 ? 1 : len);
2332 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2334 if (!movement_occurred) {
2335 add_midi_region (_view, true);
2337 _view->playlist()->thaw ();
2338 _editor->commit_reversible_command();
2343 RegionCreateDrag::aborted (bool)
2346 _view->playlist()->thaw ();
2352 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2357 , _was_selected (false)
2360 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2364 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2366 Gdk::Cursor* cursor;
2367 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2369 float x_fraction = cnote->mouse_x_fraction ();
2371 if (x_fraction > 0.0 && x_fraction < 0.25) {
2372 cursor = _editor->cursors()->left_side_trim;
2375 cursor = _editor->cursors()->right_side_trim;
2379 Drag::start_grab (event, cursor);
2381 region = &cnote->region_view();
2384 temp = region->snap_to_pixel (cnote->x0 (), true);
2385 _snap_delta = temp - cnote->x0 ();
2389 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2394 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2395 if (ms.size() > 1) {
2396 /* has to be relative, may make no sense otherwise */
2400 if (!(_was_selected = cnote->selected())) {
2402 /* tertiary-click means extend selection - we'll do that on button release,
2403 so don't add it here, because otherwise we make it hard to figure
2404 out the "extend-to" range.
2407 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2410 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2413 region->note_selected (cnote, true);
2415 _editor->get_selection().clear_points();
2416 region->unique_select (cnote);
2423 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2425 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2427 _editor->begin_reversible_command (_("resize notes"));
2429 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2430 MidiRegionSelection::iterator next;
2433 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2435 mrv->begin_resizing (at_front);
2441 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2442 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2444 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2448 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2450 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2451 if (_editor->snap_mode () != SnapOff) {
2455 if (_editor->snap_mode () == SnapOff) {
2457 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2458 if (apply_snap_delta) {
2464 if (apply_snap_delta) {
2468 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2474 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2476 if (!movement_occurred) {
2477 /* no motion - select note */
2478 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2479 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2480 _editor->current_mouse_mode() == Editing::MouseDraw) {
2482 bool changed = false;
2484 if (_was_selected) {
2485 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2487 region->note_deselected (cnote);
2490 _editor->get_selection().clear_points();
2491 region->unique_select (cnote);
2495 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2496 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2498 if (!extend && !add && region->selection_size() > 1) {
2499 _editor->get_selection().clear_points();
2500 region->unique_select (cnote);
2502 } else if (extend) {
2503 region->note_selected (cnote, true, true);
2506 /* it was added during button press */
2512 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2513 _editor->commit_reversible_selection_op();
2520 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2521 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2522 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2524 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2527 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2529 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2530 if (_editor->snap_mode () != SnapOff) {
2534 if (_editor->snap_mode () == SnapOff) {
2536 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2537 if (apply_snap_delta) {
2543 if (apply_snap_delta) {
2547 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2551 _editor->commit_reversible_command ();
2555 NoteResizeDrag::aborted (bool)
2557 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2558 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2559 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2561 mrv->abort_resizing ();
2566 AVDraggingView::AVDraggingView (RegionView* v)
2569 initial_position = v->region()->position ();
2572 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2575 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2578 TrackViewList empty;
2580 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2581 std::list<RegionView*> views = rs.by_layer();
2584 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2585 RegionView* rv = (*i);
2586 if (!rv->region()->video_locked()) {
2589 if (rv->region()->locked()) {
2592 _views.push_back (AVDraggingView (rv));
2597 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2599 Drag::start_grab (event);
2600 if (_editor->session() == 0) {
2604 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2610 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2614 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2615 _max_backwards_drag = (
2616 ARDOUR_UI::instance()->video_timeline->get_duration()
2617 + ARDOUR_UI::instance()->video_timeline->get_offset()
2618 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2621 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2622 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2623 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2626 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2629 Timecode::Time timecode;
2630 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2631 snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2632 show_verbose_cursor_text (buf);
2636 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2638 if (_editor->session() == 0) {
2641 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2645 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2649 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2650 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2652 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2653 dt = - _max_backwards_drag;
2656 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2657 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2659 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2660 RegionView* rv = i->view;
2661 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2664 rv->region()->clear_changes ();
2665 rv->region()->suspend_property_changes();
2667 rv->region()->set_position(i->initial_position + dt);
2668 rv->region_changed(ARDOUR::Properties::position);
2671 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2672 Timecode::Time timecode;
2673 Timecode::Time timediff;
2675 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2676 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2677 snprintf (buf, sizeof (buf),
2678 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2679 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2680 , _("Video Start:"),
2681 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2683 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2685 show_verbose_cursor_text (buf);
2689 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2691 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2698 if (!movement_occurred || ! _editor->session()) {
2702 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2704 _editor->begin_reversible_command (_("Move Video"));
2706 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2707 ARDOUR_UI::instance()->video_timeline->save_undo();
2708 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2709 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2711 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2712 i->view->drag_end();
2713 i->view->region()->resume_property_changes ();
2715 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2718 _editor->session()->maybe_update_session_range(
2719 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2720 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2724 _editor->commit_reversible_command ();
2728 VideoTimeLineDrag::aborted (bool)
2730 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2733 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2734 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2736 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2737 i->view->region()->resume_property_changes ();
2738 i->view->region()->set_position(i->initial_position);
2742 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2743 : RegionDrag (e, i, p, v)
2744 , _operation (StartTrim)
2745 , _preserve_fade_anchor (preserve_fade_anchor)
2746 , _jump_position_when_done (false)
2748 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2752 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2755 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2756 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2758 if (tv && tv->is_track()) {
2759 speed = tv->track()->speed();
2762 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2763 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2764 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2766 framepos_t const pf = adjusted_current_frame (event);
2767 setup_snap_delta (region_start);
2769 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2770 /* Move the contents of the region around without changing the region bounds */
2771 _operation = ContentsTrim;
2772 Drag::start_grab (event, _editor->cursors()->trimmer);
2774 /* These will get overridden for a point trim.*/
2775 if (pf < (region_start + region_length/2)) {
2776 /* closer to front */
2777 _operation = StartTrim;
2778 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2779 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2781 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2785 _operation = EndTrim;
2786 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2787 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2789 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2793 /* jump trim disabled for now
2794 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2795 _jump_position_when_done = true;
2799 switch (_operation) {
2801 show_verbose_cursor_time (region_start);
2804 show_verbose_cursor_duration (region_start, region_end);
2807 show_verbose_cursor_time (pf);
2811 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2812 i->view->region()->suspend_property_changes ();
2817 TrimDrag::motion (GdkEvent* event, bool first_move)
2819 RegionView* rv = _primary;
2822 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2823 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2824 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2825 frameoffset_t frame_delta = 0;
2827 if (tv && tv->is_track()) {
2828 speed = tv->track()->speed();
2830 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2831 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2837 switch (_operation) {
2839 trim_type = "Region start trim";
2842 trim_type = "Region end trim";
2845 trim_type = "Region content trim";
2852 _editor->begin_reversible_command (trim_type);
2854 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2855 RegionView* rv = i->view;
2856 rv->enable_display (false);
2857 rv->region()->playlist()->clear_owned_changes ();
2859 if (_operation == StartTrim) {
2860 rv->trim_front_starting ();
2863 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2866 arv->temporarily_hide_envelope ();
2870 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2871 insert_result = _editor->motion_frozen_playlists.insert (pl);
2873 if (insert_result.second) {
2879 bool non_overlap_trim = false;
2881 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2882 non_overlap_trim = true;
2885 /* contstrain trim to fade length */
2886 if (_preserve_fade_anchor) {
2887 switch (_operation) {
2889 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2890 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2892 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2893 if (ar->locked()) continue;
2894 framecnt_t len = ar->fade_in()->back()->when;
2895 if (len < dt) dt = min(dt, len);
2899 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2900 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2902 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2903 if (ar->locked()) continue;
2904 framecnt_t len = ar->fade_out()->back()->when;
2905 if (len < -dt) dt = max(dt, -len);
2914 switch (_operation) {
2916 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2917 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2918 if (changed && _preserve_fade_anchor) {
2919 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2921 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2922 framecnt_t len = ar->fade_in()->back()->when;
2923 framecnt_t diff = ar->first_frame() - i->initial_position;
2924 framepos_t new_length = len - diff;
2925 i->anchored_fade_length = min (ar->length(), new_length);
2926 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2927 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2934 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2935 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2936 if (changed && _preserve_fade_anchor) {
2937 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2939 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2940 framecnt_t len = ar->fade_out()->back()->when;
2941 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2942 framepos_t new_length = len + diff;
2943 i->anchored_fade_length = min (ar->length(), new_length);
2944 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2945 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2953 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2955 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2956 i->view->move_contents (frame_delta);
2962 switch (_operation) {
2964 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2967 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2970 // show_verbose_cursor_time (frame_delta);
2976 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2978 if (movement_occurred) {
2979 motion (event, false);
2981 if (_operation == StartTrim) {
2982 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2984 /* This must happen before the region's StatefulDiffCommand is created, as it may
2985 `correct' (ahem) the region's _start from being negative to being zero. It
2986 needs to be zero in the undo record.
2988 i->view->trim_front_ending ();
2990 if (_preserve_fade_anchor) {
2991 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2993 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2994 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2995 ar->set_fade_in_length(i->anchored_fade_length);
2996 ar->set_fade_in_active(true);
2999 if (_jump_position_when_done) {
3000 i->view->region()->set_position (i->initial_position);
3003 } else if (_operation == EndTrim) {
3004 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3005 if (_preserve_fade_anchor) {
3006 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3008 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3009 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3010 ar->set_fade_out_length(i->anchored_fade_length);
3011 ar->set_fade_out_active(true);
3014 if (_jump_position_when_done) {
3015 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3020 if (!_views.empty()) {
3021 if (_operation == StartTrim) {
3022 _editor->maybe_locate_with_edit_preroll(
3023 _views.begin()->view->region()->position());
3025 if (_operation == EndTrim) {
3026 _editor->maybe_locate_with_edit_preroll(
3027 _views.begin()->view->region()->position() +
3028 _views.begin()->view->region()->length());
3032 if (!_editor->selection->selected (_primary)) {
3033 _primary->thaw_after_trim ();
3036 set<boost::shared_ptr<Playlist> > diffed_playlists;
3038 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3039 i->view->thaw_after_trim ();
3040 i->view->enable_display (true);
3042 /* Trimming one region may affect others on the playlist, so we need
3043 to get undo Commands from the whole playlist rather than just the
3044 region. Use diffed_playlists to make sure we don't diff a given
3045 playlist more than once.
3047 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3048 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3049 vector<Command*> cmds;
3051 _editor->session()->add_commands (cmds);
3052 diffed_playlists.insert (p);
3057 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3061 _editor->motion_frozen_playlists.clear ();
3062 _editor->commit_reversible_command();
3065 /* no mouse movement */
3066 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3067 _editor->point_trim (event, adjusted_current_frame (event));
3071 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3072 i->view->region()->resume_property_changes ();
3077 TrimDrag::aborted (bool movement_occurred)
3079 /* Our motion method is changing model state, so use the Undo system
3080 to cancel. Perhaps not ideal, as this will leave an Undo point
3081 behind which may be slightly odd from the user's point of view.
3086 if (movement_occurred) {
3090 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3091 i->view->region()->resume_property_changes ();
3096 TrimDrag::setup_pointer_frame_offset ()
3098 list<DraggingView>::iterator i = _views.begin ();
3099 while (i != _views.end() && i->view != _primary) {
3103 if (i == _views.end()) {
3107 switch (_operation) {
3109 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3112 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3119 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3122 , _old_snap_type (e->snap_type())
3123 , _old_snap_mode (e->snap_mode())
3126 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3127 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3129 _real_section = &_marker->meter();
3134 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3136 Drag::start_grab (event, cursor);
3137 show_verbose_cursor_time (adjusted_current_frame(event));
3141 MeterMarkerDrag::setup_pointer_frame_offset ()
3143 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3147 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3150 // create a dummy marker to catch events, then hide it.
3153 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3155 _marker = new MeterMarker (
3157 *_editor->meter_group,
3158 UIConfiguration::instance().color ("meter marker"),
3160 *new MeterSection (_marker->meter())
3163 /* use the new marker for the grab */
3164 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3167 TempoMap& map (_editor->session()->tempo_map());
3168 /* get current state */
3169 before_state = &map.get_state();
3172 _editor->begin_reversible_command (_("move meter mark"));
3174 _editor->begin_reversible_command (_("copy meter mark"));
3176 Timecode::BBT_Time bbt = _real_section->bbt();
3178 /* we can't add a meter where one currently exists */
3179 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3184 const double beat = map.beat_at_bbt (bbt);
3185 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3186 , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style());
3189 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3190 if (_real_section->position_lock_style() != AudioTime) {
3191 _editor->set_snap_to (SnapToBar);
3192 _editor->set_snap_mode (SnapNormal);
3196 framepos_t pf = adjusted_current_frame (event);
3198 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3199 /* never snap to music for audio locked */
3200 pf = adjusted_current_frame (event, false);
3203 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3205 /* fake marker meeds to stay under the mouse, unlike the real one. */
3206 _marker->set_position (adjusted_current_frame (event, false));
3208 show_verbose_cursor_time (_real_section->frame());
3212 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3214 if (!movement_occurred) {
3215 if (was_double_click()) {
3216 _editor->edit_meter_marker (*_marker);
3221 /* reinstate old snap setting */
3222 _editor->set_snap_to (_old_snap_type);
3223 _editor->set_snap_mode (_old_snap_mode);
3225 TempoMap& map (_editor->session()->tempo_map());
3227 XMLNode &after = map.get_state();
3228 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3229 _editor->commit_reversible_command ();
3231 // delete the dummy marker we used for visual representation while moving.
3232 // a new visual marker will show up automatically.
3237 MeterMarkerDrag::aborted (bool moved)
3239 _marker->set_position (_marker->meter().frame ());
3241 /* reinstate old snap setting */
3242 _editor->set_snap_to (_old_snap_type);
3243 _editor->set_snap_mode (_old_snap_mode);
3245 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3246 // delete the dummy marker we used for visual representation while moving.
3247 // a new visual marker will show up automatically.
3252 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3257 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3259 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3260 _real_section = &_marker->tempo();
3261 _movable = _real_section->movable();
3266 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3268 Drag::start_grab (event, cursor);
3269 if (!_real_section->active()) {
3270 show_verbose_cursor_text (_("inactive"));
3272 show_verbose_cursor_time (adjusted_current_frame (event));
3277 TempoMarkerDrag::setup_pointer_frame_offset ()
3279 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3283 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3285 if (!_real_section->active()) {
3291 // mvc drag - create a dummy marker to catch events, hide it.
3294 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3296 TempoSection section (_marker->tempo());
3298 _marker = new TempoMarker (
3300 *_editor->tempo_group,
3301 UIConfiguration::instance().color ("tempo marker"),
3303 *new TempoSection (_marker->tempo())
3306 /* use the new marker for the grab */
3307 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3310 TempoMap& map (_editor->session()->tempo_map());
3311 /* get current state */
3312 before_state = &map.get_state();
3315 _editor->begin_reversible_command (_("move tempo mark"));
3318 const framepos_t frame = adjusted_current_frame (event) + 1;
3320 _editor->begin_reversible_command (_("copy tempo mark"));
3322 if (_real_section->position_lock_style() == MusicTime) {
3323 _real_section = map.add_tempo (_marker->tempo(), map.pulse_at_frame (frame), 0, _real_section->type(), MusicTime);
3325 _real_section = map.add_tempo (_marker->tempo(), 0.0, frame, _real_section->type(), AudioTime);
3331 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3332 /* use vertical movement to alter tempo .. should be log */
3333 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3336 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3338 show_verbose_cursor_text (strs.str());
3340 } else if (_movable && !_real_section->locked_to_meter()) {
3341 TempoMap& map (_editor->session()->tempo_map());
3342 const bool was_music = _real_section->position_lock_style() == MusicTime;
3344 const framepos_t pf = adjusted_current_frame (event);
3346 if (!_editor->snap_musical()) {
3349 _real_section->set_position_lock_style (AudioTime);
3352 map.gui_move_tempo (_real_section, pf);
3355 _real_section->set_position_lock_style (MusicTime);
3361 _real_section->set_position_lock_style (MusicTime);
3364 map.gui_move_tempo (_real_section, pf);
3367 _real_section->set_position_lock_style (AudioTime);
3371 show_verbose_cursor_time (_real_section->frame());
3374 /* this has moved the bar lines themselves, so recalibrate the offset */
3375 setup_pointer_frame_offset();
3377 _marker->set_position (adjusted_current_frame (event, false));
3381 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3383 if (!_real_section->active()) {
3386 if (!movement_occurred) {
3387 if (was_double_click()) {
3388 _editor->edit_tempo_marker (*_marker);
3393 TempoMap& map (_editor->session()->tempo_map());
3395 XMLNode &after = map.get_state();
3396 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3397 _editor->commit_reversible_command ();
3399 // delete the dummy marker we used for visual representation while moving.
3400 // a new visual marker will show up automatically.
3405 TempoMarkerDrag::aborted (bool moved)
3407 _marker->set_position (_marker->tempo().frame());
3409 TempoMap& map (_editor->session()->tempo_map());
3410 map.set_state (*before_state, Stateful::current_state_version);
3411 // delete the dummy marker we used for visual representation while moving.
3412 // a new visual marker will show up automatically.
3417 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3423 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3428 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3430 Drag::start_grab (event, cursor);
3431 TempoMap& map (_editor->session()->tempo_map());
3432 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3435 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3436 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3437 show_verbose_cursor_text (sstr.str());
3438 finished (event, false);
3442 BBTRulerDrag::setup_pointer_frame_offset ()
3444 TempoMap& map (_editor->session()->tempo_map());
3445 const double beat_at_frame = map.beat_at_frame (raw_grab_frame());
3446 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3449 if (divisions > 0) {
3450 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3452 /* while it makes some sense for the user to determine the division to 'grab',
3453 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3454 and the result over steep tempo curves. Use sixteenths.
3456 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3459 _pulse = map.pulse_at_beat (beat);
3461 _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3466 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3468 TempoMap& map (_editor->session()->tempo_map());
3471 /* get current state */
3472 before_state = &map.get_state();
3473 _editor->begin_reversible_command (_("dilate tempo"));
3478 if (_editor->snap_musical()) {
3479 pf = adjusted_current_frame (event, false);
3481 pf = adjusted_current_frame (event);
3484 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3485 /* adjust previous tempo to match pointer frame */
3486 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3489 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3490 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3491 show_verbose_cursor_text (sstr.str());
3495 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3497 if (!movement_occurred) {
3501 TempoMap& map (_editor->session()->tempo_map());
3503 XMLNode &after = map.get_state();
3504 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3505 _editor->commit_reversible_command ();
3509 BBTRulerDrag::aborted (bool moved)
3512 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3517 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3518 : Drag (e, &c.track_canvas_item(), false)
3523 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3526 /** Do all the things we do when dragging the playhead to make it look as though
3527 * we have located, without actually doing the locate (because that would cause
3528 * the diskstream buffers to be refilled, which is too slow).
3531 CursorDrag::fake_locate (framepos_t t)
3533 if (_editor->session () == 0) {
3537 _editor->playhead_cursor->set_position (t);
3539 Session* s = _editor->session ();
3540 if (s->timecode_transmission_suspended ()) {
3541 framepos_t const f = _editor->playhead_cursor->current_frame ();
3542 /* This is asynchronous so it will be sent "now"
3544 s->send_mmc_locate (f);
3545 /* These are synchronous and will be sent during the next
3548 s->queue_full_time_code ();
3549 s->queue_song_position_pointer ();
3552 show_verbose_cursor_time (t);
3553 _editor->UpdateAllTransportClocks (t);
3557 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3559 Drag::start_grab (event, c);
3560 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3562 _grab_zoom = _editor->samples_per_pixel;
3564 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3566 _editor->snap_to_with_modifier (where, event);
3568 _editor->_dragging_playhead = true;
3570 Session* s = _editor->session ();
3572 /* grab the track canvas item as well */
3574 _cursor.track_canvas_item().grab();
3577 if (_was_rolling && _stop) {
3581 if (s->is_auditioning()) {
3582 s->cancel_audition ();
3586 if (AudioEngine::instance()->connected()) {
3588 /* do this only if we're the engine is connected
3589 * because otherwise this request will never be
3590 * serviced and we'll busy wait forever. likewise,
3591 * notice if we are disconnected while waiting for the
3592 * request to be serviced.
3595 s->request_suspend_timecode_transmission ();
3596 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3597 /* twiddle our thumbs */
3602 fake_locate (where - snap_delta (event->button.state));
3606 CursorDrag::motion (GdkEvent* event, bool)
3608 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3609 _editor->snap_to_with_modifier (where, event);
3610 if (where != last_pointer_frame()) {
3611 fake_locate (where - snap_delta (event->button.state));
3616 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3618 _editor->_dragging_playhead = false;
3620 _cursor.track_canvas_item().ungrab();
3622 if (!movement_occurred && _stop) {
3626 motion (event, false);
3628 Session* s = _editor->session ();
3630 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3631 _editor->_pending_locate_request = true;
3632 s->request_resume_timecode_transmission ();
3637 CursorDrag::aborted (bool)
3639 _cursor.track_canvas_item().ungrab();
3641 if (_editor->_dragging_playhead) {
3642 _editor->session()->request_resume_timecode_transmission ();
3643 _editor->_dragging_playhead = false;
3646 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3649 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3650 : RegionDrag (e, i, p, v)
3652 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3656 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3658 Drag::start_grab (event, cursor);
3660 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3661 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3662 setup_snap_delta (r->position ());
3664 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3668 FadeInDrag::setup_pointer_frame_offset ()
3670 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3671 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3672 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3676 FadeInDrag::motion (GdkEvent* event, bool)
3678 framecnt_t fade_length;
3680 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3681 _editor->snap_to_with_modifier (pos, event);
3682 pos -= snap_delta (event->button.state);
3684 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3686 if (pos < (region->position() + 64)) {
3687 fade_length = 64; // this should be a minimum defined somewhere
3688 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3689 fade_length = region->length() - region->fade_out()->back()->when - 1;
3691 fade_length = pos - region->position();
3694 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3696 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3702 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3705 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3709 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3711 if (!movement_occurred) {
3715 framecnt_t fade_length;
3716 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3717 _editor->snap_to_with_modifier (pos, event);
3718 pos -= snap_delta (event->button.state);
3720 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3722 if (pos < (region->position() + 64)) {
3723 fade_length = 64; // this should be a minimum defined somewhere
3724 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3725 fade_length = region->length() - region->fade_out()->back()->when - 1;
3727 fade_length = pos - region->position();
3730 bool in_command = false;
3732 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3734 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3740 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3741 XMLNode &before = alist->get_state();
3743 tmp->audio_region()->set_fade_in_length (fade_length);
3744 tmp->audio_region()->set_fade_in_active (true);
3747 _editor->begin_reversible_command (_("change fade in length"));
3750 XMLNode &after = alist->get_state();
3751 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3755 _editor->commit_reversible_command ();
3760 FadeInDrag::aborted (bool)
3762 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3763 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3769 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3773 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3774 : RegionDrag (e, i, p, v)
3776 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3780 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3782 Drag::start_grab (event, cursor);
3784 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3785 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3786 setup_snap_delta (r->last_frame ());
3788 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3792 FadeOutDrag::setup_pointer_frame_offset ()
3794 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3795 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3796 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3800 FadeOutDrag::motion (GdkEvent* event, bool)
3802 framecnt_t fade_length;
3804 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3805 _editor->snap_to_with_modifier (pos, event);
3806 pos -= snap_delta (event->button.state);
3808 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3810 if (pos > (region->last_frame() - 64)) {
3811 fade_length = 64; // this should really be a minimum fade defined somewhere
3812 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3813 fade_length = region->length() - region->fade_in()->back()->when - 1;
3815 fade_length = region->last_frame() - pos;
3818 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3820 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3826 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3829 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3833 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3835 if (!movement_occurred) {
3839 framecnt_t fade_length;
3841 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3842 _editor->snap_to_with_modifier (pos, event);
3843 pos -= snap_delta (event->button.state);
3845 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3847 if (pos > (region->last_frame() - 64)) {
3848 fade_length = 64; // this should really be a minimum fade defined somewhere
3849 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3850 fade_length = region->length() - region->fade_in()->back()->when - 1;
3852 fade_length = region->last_frame() - pos;
3855 bool in_command = false;
3857 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3859 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3865 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3866 XMLNode &before = alist->get_state();
3868 tmp->audio_region()->set_fade_out_length (fade_length);
3869 tmp->audio_region()->set_fade_out_active (true);
3872 _editor->begin_reversible_command (_("change fade out length"));
3875 XMLNode &after = alist->get_state();
3876 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3880 _editor->commit_reversible_command ();
3885 FadeOutDrag::aborted (bool)
3887 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3888 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3894 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3898 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3900 , _selection_changed (false)
3902 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3903 Gtk::Window* toplevel = _editor->current_toplevel();
3904 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3908 _points.push_back (ArdourCanvas::Duple (0, 0));
3910 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3913 MarkerDrag::~MarkerDrag ()
3915 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3920 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3922 location = new Location (*l);
3923 markers.push_back (m);
3928 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3930 Drag::start_grab (event, cursor);
3934 Location *location = _editor->find_location_from_marker (_marker, is_start);
3935 _editor->_dragging_edit_point = true;
3937 update_item (location);
3939 // _drag_line->show();
3940 // _line->raise_to_top();
3943 show_verbose_cursor_time (location->start());
3945 show_verbose_cursor_time (location->end());
3947 setup_snap_delta (is_start ? location->start() : location->end());
3949 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3952 case Selection::Toggle:
3953 /* we toggle on the button release */
3955 case Selection::Set:
3956 if (!_editor->selection->selected (_marker)) {
3957 _editor->selection->set (_marker);
3958 _selection_changed = true;
3961 case Selection::Extend:
3963 Locations::LocationList ll;
3964 list<ArdourMarker*> to_add;
3966 _editor->selection->markers.range (s, e);
3967 s = min (_marker->position(), s);
3968 e = max (_marker->position(), e);
3971 if (e < max_framepos) {
3974 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3975 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3976 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3979 to_add.push_back (lm->start);
3982 to_add.push_back (lm->end);
3986 if (!to_add.empty()) {
3987 _editor->selection->add (to_add);
3988 _selection_changed = true;
3992 case Selection::Add:
3993 _editor->selection->add (_marker);
3994 _selection_changed = true;
3999 /* Set up copies for us to manipulate during the drag
4002 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4004 Location* l = _editor->find_location_from_marker (*i, is_start);
4011 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4013 /* range: check that the other end of the range isn't
4016 CopiedLocationInfo::iterator x;
4017 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4018 if (*(*x).location == *l) {
4022 if (x == _copied_locations.end()) {
4023 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4025 (*x).markers.push_back (*i);
4026 (*x).move_both = true;
4034 MarkerDrag::setup_pointer_frame_offset ()
4037 Location *location = _editor->find_location_from_marker (_marker, is_start);
4038 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4042 MarkerDrag::motion (GdkEvent* event, bool)
4044 framecnt_t f_delta = 0;
4046 bool move_both = false;
4047 Location *real_location;
4048 Location *copy_location = 0;
4049 framecnt_t const sd = snap_delta (event->button.state);
4051 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4052 framepos_t next = newframe;
4054 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4058 CopiedLocationInfo::iterator x;
4060 /* find the marker we're dragging, and compute the delta */
4062 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4064 copy_location = (*x).location;
4066 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4068 /* this marker is represented by this
4069 * CopiedLocationMarkerInfo
4072 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4077 if (real_location->is_mark()) {
4078 f_delta = newframe - copy_location->start();
4082 switch (_marker->type()) {
4083 case ArdourMarker::SessionStart:
4084 case ArdourMarker::RangeStart:
4085 case ArdourMarker::LoopStart:
4086 case ArdourMarker::PunchIn:
4087 f_delta = newframe - copy_location->start();
4090 case ArdourMarker::SessionEnd:
4091 case ArdourMarker::RangeEnd:
4092 case ArdourMarker::LoopEnd:
4093 case ArdourMarker::PunchOut:
4094 f_delta = newframe - copy_location->end();
4097 /* what kind of marker is this ? */
4106 if (x == _copied_locations.end()) {
4107 /* hmm, impossible - we didn't find the dragged marker */
4111 /* now move them all */
4113 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4115 copy_location = x->location;
4117 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4121 if (real_location->locked()) {
4125 if (copy_location->is_mark()) {
4129 copy_location->set_start (copy_location->start() + f_delta);
4133 framepos_t new_start = copy_location->start() + f_delta;
4134 framepos_t new_end = copy_location->end() + f_delta;
4136 if (is_start) { // start-of-range marker
4138 if (move_both || (*x).move_both) {
4139 copy_location->set_start (new_start);
4140 copy_location->set_end (new_end);
4141 } else if (new_start < copy_location->end()) {
4142 copy_location->set_start (new_start);
4143 } else if (newframe > 0) {
4144 //_editor->snap_to (next, RoundUpAlways, true);
4145 copy_location->set_end (next);
4146 copy_location->set_start (newframe);
4149 } else { // end marker
4151 if (move_both || (*x).move_both) {
4152 copy_location->set_end (new_end);
4153 copy_location->set_start (new_start);
4154 } else if (new_end > copy_location->start()) {
4155 copy_location->set_end (new_end);
4156 } else if (newframe > 0) {
4157 //_editor->snap_to (next, RoundDownAlways, true);
4158 copy_location->set_start (next);
4159 copy_location->set_end (newframe);
4164 update_item (copy_location);
4166 /* now lookup the actual GUI items used to display this
4167 * location and move them to wherever the copy of the location
4168 * is now. This means that the logic in ARDOUR::Location is
4169 * still enforced, even though we are not (yet) modifying
4170 * the real Location itself.
4173 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4176 lm->set_position (copy_location->start(), copy_location->end());
4181 assert (!_copied_locations.empty());
4183 show_verbose_cursor_time (newframe);
4187 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4189 if (!movement_occurred) {
4191 if (was_double_click()) {
4192 _editor->rename_marker (_marker);
4196 /* just a click, do nothing but finish
4197 off the selection process
4200 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4202 case Selection::Set:
4203 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4204 _editor->selection->set (_marker);
4205 _selection_changed = true;
4209 case Selection::Toggle:
4210 /* we toggle on the button release, click only */
4211 _editor->selection->toggle (_marker);
4212 _selection_changed = true;
4216 case Selection::Extend:
4217 case Selection::Add:
4221 if (_selection_changed) {
4222 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4223 _editor->commit_reversible_selection_op();
4229 _editor->_dragging_edit_point = false;
4231 XMLNode &before = _editor->session()->locations()->get_state();
4232 bool in_command = false;
4234 MarkerSelection::iterator i;
4235 CopiedLocationInfo::iterator x;
4238 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4239 x != _copied_locations.end() && i != _editor->selection->markers.end();
4242 Location * location = _editor->find_location_from_marker (*i, is_start);
4246 if (location->locked()) {
4250 _editor->begin_reversible_command ( _("move marker") );
4253 if (location->is_mark()) {
4254 location->set_start (((*x).location)->start());
4256 location->set (((*x).location)->start(), ((*x).location)->end());
4262 XMLNode &after = _editor->session()->locations()->get_state();
4263 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4264 _editor->commit_reversible_command ();
4269 MarkerDrag::aborted (bool movement_occurred)
4271 if (!movement_occurred) {
4275 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4277 /* move all markers to their original location */
4280 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4283 Location * location = _editor->find_location_from_marker (*m, is_start);
4286 (*m)->set_position (is_start ? location->start() : location->end());
4293 MarkerDrag::update_item (Location*)
4298 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4300 , _fixed_grab_x (0.0)
4301 , _fixed_grab_y (0.0)
4302 , _cumulative_x_drag (0.0)
4303 , _cumulative_y_drag (0.0)
4307 if (_zero_gain_fraction < 0.0) {
4308 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4311 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4313 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4319 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4321 Drag::start_grab (event, _editor->cursors()->fader);
4323 // start the grab at the center of the control point so
4324 // the point doesn't 'jump' to the mouse after the first drag
4325 _fixed_grab_x = _point->get_x();
4326 _fixed_grab_y = _point->get_y();
4328 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4329 setup_snap_delta (pos);
4331 float const fraction = 1 - (_point->get_y() / _point->line().height());
4332 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4334 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4336 if (!_point->can_slide ()) {
4337 _x_constrained = true;
4342 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4344 double dx = _drags->current_pointer_x() - last_pointer_x();
4345 double dy = current_pointer_y() - last_pointer_y();
4346 bool need_snap = true;
4348 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4354 /* coordinate in pixels relative to the start of the region (for region-based automation)
4355 or track (for track-based automation) */
4356 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4357 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4359 // calculate zero crossing point. back off by .01 to stay on the
4360 // positive side of zero
4361 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4363 if (_x_constrained) {
4366 if (_y_constrained) {
4370 _cumulative_x_drag = cx - _fixed_grab_x;
4371 _cumulative_y_drag = cy - _fixed_grab_y;
4375 cy = min ((double) _point->line().height(), cy);
4377 // make sure we hit zero when passing through
4378 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4382 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4384 if (!_x_constrained && need_snap) {
4385 _editor->snap_to_with_modifier (cx_frames, event);
4388 cx_frames -= snap_delta (event->button.state);
4389 cx_frames = min (cx_frames, _point->line().maximum_time());
4391 float const fraction = 1.0 - (cy / _point->line().height());
4394 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4395 _editor->begin_reversible_command (_("automation event move"));
4396 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4398 pair<double, float> result;
4399 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4401 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4405 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4407 if (!movement_occurred) {
4410 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4411 _editor->reset_point_selection ();
4415 _point->line().end_drag (_pushing, _final_index);
4416 _editor->commit_reversible_command ();
4421 ControlPointDrag::aborted (bool)
4423 _point->line().reset ();
4427 ControlPointDrag::active (Editing::MouseMode m)
4429 if (m == Editing::MouseDraw) {
4430 /* always active in mouse draw */
4434 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4435 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4438 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4441 , _fixed_grab_x (0.0)
4442 , _fixed_grab_y (0.0)
4443 , _cumulative_y_drag (0)
4447 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4451 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4453 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4456 _item = &_line->grab_item ();
4458 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4459 origin, and ditto for y.
4462 double mx = event->button.x;
4463 double my = event->button.y;
4465 _line->grab_item().canvas_to_item (mx, my);
4467 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4469 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4470 /* no adjacent points */
4474 Drag::start_grab (event, _editor->cursors()->fader);
4476 /* store grab start in item frame */
4477 double const bx = _line->nth (_before)->get_x();
4478 double const ax = _line->nth (_after)->get_x();
4479 double const click_ratio = (ax - mx) / (ax - bx);
4481 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4486 double fraction = 1.0 - (cy / _line->height());
4488 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4492 LineDrag::motion (GdkEvent* event, bool first_move)
4494 double dy = current_pointer_y() - last_pointer_y();
4496 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4500 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4502 _cumulative_y_drag = cy - _fixed_grab_y;
4505 cy = min ((double) _line->height(), cy);
4507 double const fraction = 1.0 - (cy / _line->height());
4511 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4513 _editor->begin_reversible_command (_("automation range move"));
4514 _line->start_drag_line (_before, _after, initial_fraction);
4517 /* we are ignoring x position for this drag, so we can just pass in anything */
4518 pair<double, float> result;
4520 result = _line->drag_motion (0, fraction, true, false, ignored);
4521 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4525 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4527 if (movement_occurred) {
4528 motion (event, false);
4529 _line->end_drag (false, 0);
4530 _editor->commit_reversible_command ();
4532 /* add a new control point on the line */
4534 AutomationTimeAxisView* atv;
4536 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4537 framepos_t where = grab_frame ();
4540 double cy = _fixed_grab_y;
4542 _line->grab_item().item_to_canvas (cx, cy);
4544 atv->add_automation_event (event, where, cy, false);
4545 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4546 AudioRegionView* arv;
4548 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4549 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4556 LineDrag::aborted (bool)
4561 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4565 _region_view_grab_x (0.0),
4566 _cumulative_x_drag (0),
4570 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4574 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4576 Drag::start_grab (event);
4578 _line = reinterpret_cast<Line*> (_item);
4581 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4583 double cx = event->button.x;
4584 double cy = event->button.y;
4586 _item->parent()->canvas_to_item (cx, cy);
4588 /* store grab start in parent frame */
4589 _region_view_grab_x = cx;
4591 _before = *(float*) _item->get_data ("position");
4593 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4595 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4599 FeatureLineDrag::motion (GdkEvent*, bool)
4601 double dx = _drags->current_pointer_x() - last_pointer_x();
4603 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4605 _cumulative_x_drag += dx;
4607 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4616 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4618 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4620 float *pos = new float;
4623 _line->set_data ("position", pos);
4629 FeatureLineDrag::finished (GdkEvent*, bool)
4631 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4632 _arv->update_transient(_before, _before);
4636 FeatureLineDrag::aborted (bool)
4641 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4643 , _vertical_only (false)
4645 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4649 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4651 Drag::start_grab (event);
4652 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4656 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4663 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4665 framepos_t grab = grab_frame ();
4666 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4667 _editor->snap_to_with_modifier (grab, event);
4669 grab = raw_grab_frame ();
4672 /* base start and end on initial click position */
4682 if (current_pointer_y() < grab_y()) {
4683 y1 = current_pointer_y();
4686 y2 = current_pointer_y();
4690 if (start != end || y1 != y2) {
4692 double x1 = _editor->sample_to_pixel (start);
4693 double x2 = _editor->sample_to_pixel (end);
4694 const double min_dimension = 2.0;
4696 if (_vertical_only) {
4697 /* fixed 10 pixel width */
4701 x2 = min (x1 - min_dimension, x2);
4703 x2 = max (x1 + min_dimension, x2);
4708 y2 = min (y1 - min_dimension, y2);
4710 y2 = max (y1 + min_dimension, y2);
4713 /* translate rect into item space and set */
4715 ArdourCanvas::Rect r (x1, y1, x2, y2);
4717 /* this drag is a _trackview_only == true drag, so the y1 and
4718 * y2 (computed using current_pointer_y() and grab_y()) will be
4719 * relative to the top of the trackview group). The
4720 * rubberband rect has the same parent/scroll offset as the
4721 * the trackview group, so we can use the "r" rect directly
4722 * to set the shape of the rubberband.
4725 _editor->rubberband_rect->set (r);
4726 _editor->rubberband_rect->show();
4727 _editor->rubberband_rect->raise_to_top();
4729 show_verbose_cursor_time (pf);
4731 do_select_things (event, true);
4736 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4740 framepos_t grab = grab_frame ();
4741 framepos_t lpf = last_pointer_frame ();
4743 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4744 grab = raw_grab_frame ();
4745 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4759 if (current_pointer_y() < grab_y()) {
4760 y1 = current_pointer_y();
4763 y2 = current_pointer_y();
4767 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4771 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4773 if (movement_occurred) {
4775 motion (event, false);
4776 do_select_things (event, false);
4782 bool do_deselect = true;
4783 MidiTimeAxisView* mtv;
4785 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4787 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4788 /* nothing selected */
4789 add_midi_region (mtv, true);
4790 do_deselect = false;
4794 /* do not deselect if Primary or Tertiary (toggle-select or
4795 * extend-select are pressed.
4798 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4799 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4806 _editor->rubberband_rect->hide();
4810 RubberbandSelectDrag::aborted (bool)
4812 _editor->rubberband_rect->hide ();
4815 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4816 : RegionDrag (e, i, p, v)
4818 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4822 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4824 Drag::start_grab (event, cursor);
4826 _editor->get_selection().add (_primary);
4828 framepos_t where = _primary->region()->position();
4829 setup_snap_delta (where);
4831 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4835 TimeFXDrag::motion (GdkEvent* event, bool)
4837 RegionView* rv = _primary;
4838 StreamView* cv = rv->get_time_axis_view().view ();
4840 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4841 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4842 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4843 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4844 _editor->snap_to_with_modifier (pf, event);
4845 pf -= snap_delta (event->button.state);
4847 if (pf > rv->region()->position()) {
4848 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4851 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4855 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4857 /* this may have been a single click, no drag. We still want the dialog
4858 to show up in that case, so that the user can manually edit the
4859 parameters for the timestretch.
4862 float fraction = 1.0;
4864 if (movement_occurred) {
4866 motion (event, false);
4868 _primary->get_time_axis_view().hide_timestretch ();
4870 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4872 if (adjusted_frame_pos < _primary->region()->position()) {
4873 /* backwards drag of the left edge - not usable */
4877 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4879 fraction = (double) newlen / (double) _primary->region()->length();
4881 #ifndef USE_RUBBERBAND
4882 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4883 if (_primary->region()->data_type() == DataType::AUDIO) {
4884 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4889 if (!_editor->get_selection().regions.empty()) {
4890 /* primary will already be included in the selection, and edit
4891 group shared editing will propagate selection across
4892 equivalent regions, so just use the current region
4896 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4897 error << _("An error occurred while executing time stretch operation") << endmsg;
4903 TimeFXDrag::aborted (bool)
4905 _primary->get_time_axis_view().hide_timestretch ();
4908 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4911 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4915 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4917 Drag::start_grab (event);
4921 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4923 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4927 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4929 if (movement_occurred && _editor->session()) {
4930 /* make sure we stop */
4931 _editor->session()->request_transport_speed (0.0);
4936 ScrubDrag::aborted (bool)
4941 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4945 , _time_selection_at_start (!_editor->get_selection().time.empty())
4947 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4949 if (_time_selection_at_start) {
4950 start_at_start = _editor->get_selection().time.start();
4951 end_at_start = _editor->get_selection().time.end_frame();
4956 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4958 if (_editor->session() == 0) {
4962 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4964 switch (_operation) {
4965 case CreateSelection:
4966 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4971 cursor = _editor->cursors()->selector;
4972 Drag::start_grab (event, cursor);
4975 case SelectionStartTrim:
4976 if (_editor->clicked_axisview) {
4977 _editor->clicked_axisview->order_selection_trims (_item, true);
4979 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4982 case SelectionEndTrim:
4983 if (_editor->clicked_axisview) {
4984 _editor->clicked_axisview->order_selection_trims (_item, false);
4986 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4990 Drag::start_grab (event, cursor);
4993 case SelectionExtend:
4994 Drag::start_grab (event, cursor);
4998 if (_operation == SelectionMove) {
4999 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5001 show_verbose_cursor_time (adjusted_current_frame (event));
5006 SelectionDrag::setup_pointer_frame_offset ()
5008 switch (_operation) {
5009 case CreateSelection:
5010 _pointer_frame_offset = 0;
5013 case SelectionStartTrim:
5015 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5018 case SelectionEndTrim:
5019 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5022 case SelectionExtend:
5028 SelectionDrag::motion (GdkEvent* event, bool first_move)
5030 framepos_t start = 0;
5032 framecnt_t length = 0;
5033 framecnt_t distance = 0;
5035 framepos_t const pending_position = adjusted_current_frame (event);
5037 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5041 switch (_operation) {
5042 case CreateSelection:
5044 framepos_t grab = grab_frame ();
5047 grab = adjusted_current_frame (event, false);
5048 if (grab < pending_position) {
5049 _editor->snap_to (grab, RoundDownMaybe);
5051 _editor->snap_to (grab, RoundUpMaybe);
5055 if (pending_position < grab) {
5056 start = pending_position;
5059 end = pending_position;
5063 /* first drag: Either add to the selection
5064 or create a new selection
5071 /* adding to the selection */
5072 _editor->set_selected_track_as_side_effect (Selection::Add);
5073 _editor->clicked_selection = _editor->selection->add (start, end);
5080 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5081 _editor->set_selected_track_as_side_effect (Selection::Set);
5084 _editor->clicked_selection = _editor->selection->set (start, end);
5088 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5089 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5090 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5092 _editor->selection->add (atest);
5096 /* select all tracks within the rectangle that we've marked out so far */
5097 TrackViewList new_selection;
5098 TrackViewList& all_tracks (_editor->track_views);
5100 ArdourCanvas::Coord const top = grab_y();
5101 ArdourCanvas::Coord const bottom = current_pointer_y();
5103 if (top >= 0 && bottom >= 0) {
5105 //first, find the tracks that are covered in the y range selection
5106 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5107 if ((*i)->covered_by_y_range (top, bottom)) {
5108 new_selection.push_back (*i);
5112 //now find any tracks that are GROUPED with the tracks we selected
5113 TrackViewList grouped_add = new_selection;
5114 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5115 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5116 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
5117 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5118 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5119 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5120 grouped_add.push_back (*j);
5125 //now compare our list with the current selection, and add or remove as necessary
5126 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5127 TrackViewList tracks_to_add;
5128 TrackViewList tracks_to_remove;
5129 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5130 if ( !_editor->selection->tracks.contains ( *i ) )
5131 tracks_to_add.push_back ( *i );
5132 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5133 if ( !grouped_add.contains ( *i ) )
5134 tracks_to_remove.push_back ( *i );
5135 _editor->selection->add(tracks_to_add);
5136 _editor->selection->remove(tracks_to_remove);
5142 case SelectionStartTrim:
5144 end = _editor->selection->time[_editor->clicked_selection].end;
5146 if (pending_position > end) {
5149 start = pending_position;
5153 case SelectionEndTrim:
5155 start = _editor->selection->time[_editor->clicked_selection].start;
5157 if (pending_position < start) {
5160 end = pending_position;
5167 start = _editor->selection->time[_editor->clicked_selection].start;
5168 end = _editor->selection->time[_editor->clicked_selection].end;
5170 length = end - start;
5171 distance = pending_position - start;
5172 start = pending_position;
5173 _editor->snap_to (start);
5175 end = start + length;
5179 case SelectionExtend:
5184 switch (_operation) {
5186 if (_time_selection_at_start) {
5187 _editor->selection->move_time (distance);
5191 _editor->selection->replace (_editor->clicked_selection, start, end);
5195 if (_operation == SelectionMove) {
5196 show_verbose_cursor_time(start);
5198 show_verbose_cursor_time(pending_position);
5203 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5205 Session* s = _editor->session();
5207 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5208 if (movement_occurred) {
5209 motion (event, false);
5210 /* XXX this is not object-oriented programming at all. ick */
5211 if (_editor->selection->time.consolidate()) {
5212 _editor->selection->TimeChanged ();
5215 /* XXX what if its a music time selection? */
5217 if (s->get_play_range() && s->transport_rolling()) {
5218 s->request_play_range (&_editor->selection->time, true);
5219 } else if (!s->config.get_external_sync()) {
5220 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5221 if (_operation == SelectionEndTrim)
5222 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5224 s->request_locate (_editor->get_selection().time.start());
5228 if (_editor->get_selection().time.length() != 0) {
5229 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5231 s->clear_range_selection ();
5236 /* just a click, no pointer movement.
5239 if (_operation == SelectionExtend) {
5240 if (_time_selection_at_start) {
5241 framepos_t pos = adjusted_current_frame (event, false);
5242 framepos_t start = min (pos, start_at_start);
5243 framepos_t end = max (pos, end_at_start);
5244 _editor->selection->set (start, end);
5247 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5248 if (_editor->clicked_selection) {
5249 _editor->selection->remove (_editor->clicked_selection);
5252 if (!_editor->clicked_selection) {
5253 _editor->selection->clear_time();
5258 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5259 _editor->selection->set (_editor->clicked_axisview);
5262 if (s && s->get_play_range () && s->transport_rolling()) {
5263 s->request_stop (false, false);
5268 _editor->stop_canvas_autoscroll ();
5269 _editor->clicked_selection = 0;
5270 _editor->commit_reversible_selection_op ();
5274 SelectionDrag::aborted (bool)
5279 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5280 : Drag (e, i, false),
5284 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5286 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5287 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5288 physical_screen_height (_editor->current_toplevel()->get_window())));
5289 _drag_rect->hide ();
5291 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5292 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5295 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5297 /* normal canvas items will be cleaned up when their parent group is deleted. But
5298 this item is created as the child of a long-lived parent group, and so we
5299 need to explicitly delete it.
5305 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5307 if (_editor->session() == 0) {
5311 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5313 if (!_editor->temp_location) {
5314 _editor->temp_location = new Location (*_editor->session());
5317 switch (_operation) {
5318 case CreateSkipMarker:
5319 case CreateRangeMarker:
5320 case CreateTransportMarker:
5321 case CreateCDMarker:
5323 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5328 cursor = _editor->cursors()->selector;
5332 Drag::start_grab (event, cursor);
5334 show_verbose_cursor_time (adjusted_current_frame (event));
5338 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5340 framepos_t start = 0;
5342 ArdourCanvas::Rectangle *crect;
5344 switch (_operation) {
5345 case CreateSkipMarker:
5346 crect = _editor->range_bar_drag_rect;
5348 case CreateRangeMarker:
5349 crect = _editor->range_bar_drag_rect;
5351 case CreateTransportMarker:
5352 crect = _editor->transport_bar_drag_rect;
5354 case CreateCDMarker:
5355 crect = _editor->cd_marker_bar_drag_rect;
5358 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5363 framepos_t const pf = adjusted_current_frame (event);
5365 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5366 framepos_t grab = grab_frame ();
5367 _editor->snap_to (grab);
5369 if (pf < grab_frame()) {
5377 /* first drag: Either add to the selection
5378 or create a new selection.
5383 _editor->temp_location->set (start, end);
5387 update_item (_editor->temp_location);
5389 //_drag_rect->raise_to_top();
5395 _editor->temp_location->set (start, end);
5397 double x1 = _editor->sample_to_pixel (start);
5398 double x2 = _editor->sample_to_pixel (end);
5402 update_item (_editor->temp_location);
5405 show_verbose_cursor_time (pf);
5410 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5412 Location * newloc = 0;
5416 if (movement_occurred) {
5417 motion (event, false);
5420 switch (_operation) {
5421 case CreateSkipMarker:
5422 case CreateRangeMarker:
5423 case CreateCDMarker:
5425 XMLNode &before = _editor->session()->locations()->get_state();
5426 if (_operation == CreateSkipMarker) {
5427 _editor->begin_reversible_command (_("new skip marker"));
5428 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5429 flags = Location::IsRangeMarker | Location::IsSkip;
5430 _editor->range_bar_drag_rect->hide();
5431 } else if (_operation == CreateCDMarker) {
5432 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5433 _editor->begin_reversible_command (_("new CD marker"));
5434 flags = Location::IsRangeMarker | Location::IsCDMarker;
5435 _editor->cd_marker_bar_drag_rect->hide();
5437 _editor->begin_reversible_command (_("new skip marker"));
5438 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5439 flags = Location::IsRangeMarker;
5440 _editor->range_bar_drag_rect->hide();
5442 newloc = new Location (
5443 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5446 _editor->session()->locations()->add (newloc, true);
5447 XMLNode &after = _editor->session()->locations()->get_state();
5448 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5449 _editor->commit_reversible_command ();
5453 case CreateTransportMarker:
5454 // popup menu to pick loop or punch
5455 _editor->new_transport_marker_context_menu (&event->button, _item);
5461 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5463 if (_operation == CreateTransportMarker) {
5465 /* didn't drag, so just locate */
5467 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5469 } else if (_operation == CreateCDMarker) {
5471 /* didn't drag, but mark is already created so do
5474 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5479 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5481 if (end == max_framepos) {
5482 end = _editor->session()->current_end_frame ();
5485 if (start == max_framepos) {
5486 start = _editor->session()->current_start_frame ();
5489 switch (_editor->mouse_mode) {
5491 /* find the two markers on either side and then make the selection from it */
5492 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5496 /* find the two markers on either side of the click and make the range out of it */
5497 _editor->selection->set (start, end);
5506 _editor->stop_canvas_autoscroll ();
5510 RangeMarkerBarDrag::aborted (bool movement_occurred)
5512 if (movement_occurred) {
5513 _drag_rect->hide ();
5518 RangeMarkerBarDrag::update_item (Location* location)
5520 double const x1 = _editor->sample_to_pixel (location->start());
5521 double const x2 = _editor->sample_to_pixel (location->end());
5523 _drag_rect->set_x0 (x1);
5524 _drag_rect->set_x1 (x2);
5527 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5529 , _cumulative_dx (0)
5530 , _cumulative_dy (0)
5531 , _was_selected (false)
5533 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5535 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5537 _region = &_primary->region_view ();
5538 _note_height = _region->midi_stream_view()->note_height ();
5542 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5544 Drag::start_grab (event);
5545 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5547 if (!(_was_selected = _primary->selected())) {
5549 /* tertiary-click means extend selection - we'll do that on button release,
5550 so don't add it here, because otherwise we make it hard to figure
5551 out the "extend-to" range.
5554 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5557 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5560 _region->note_selected (_primary, true);
5562 _editor->get_selection().clear_points();
5563 _region->unique_select (_primary);
5569 /** @return Current total drag x change in frames */
5571 NoteDrag::total_dx (const guint state) const
5574 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5576 /* primary note time */
5577 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5579 /* new time of the primary note in session frames */
5580 frameoffset_t st = n + dx + snap_delta (state);
5582 framepos_t const rp = _region->region()->position ();
5584 /* prevent the note being dragged earlier than the region's position */
5587 /* possibly snap and return corresponding delta */
5591 if (ArdourKeyboard::indicates_snap (state)) {
5592 if (_editor->snap_mode () != SnapOff) {
5596 if (_editor->snap_mode () == SnapOff) {
5598 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5599 if (ArdourKeyboard::indicates_snap_delta (state)) {
5607 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5608 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5610 ret = st - n - snap_delta (state);
5615 /** @return Current total drag y change in note number */
5617 NoteDrag::total_dy () const
5619 MidiStreamView* msv = _region->midi_stream_view ();
5620 double const y = _region->midi_view()->y_position ();
5621 /* new current note */
5622 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5624 n = max (msv->lowest_note(), n);
5625 n = min (msv->highest_note(), n);
5626 /* and work out delta */
5627 return n - msv->y_to_note (grab_y() - y);
5631 NoteDrag::motion (GdkEvent * event, bool)
5633 /* Total change in x and y since the start of the drag */
5634 frameoffset_t const dx = total_dx (event->button.state);
5635 int8_t const dy = total_dy ();
5637 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5638 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5639 double const tdy = -dy * _note_height - _cumulative_dy;
5642 _cumulative_dx += tdx;
5643 _cumulative_dy += tdy;
5645 int8_t note_delta = total_dy();
5647 _region->move_selection (tdx, tdy, note_delta);
5649 /* the new note value may be the same as the old one, but we
5650 * don't know what that means because the selection may have
5651 * involved more than one note and we might be doing something
5652 * odd with them. so show the note value anyway, always.
5655 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5657 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5662 NoteDrag::finished (GdkEvent* ev, bool moved)
5665 /* no motion - select note */
5667 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5668 _editor->current_mouse_mode() == Editing::MouseDraw) {
5670 bool changed = false;
5672 if (_was_selected) {
5673 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5675 _region->note_deselected (_primary);
5678 _editor->get_selection().clear_points();
5679 _region->unique_select (_primary);
5683 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5684 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5686 if (!extend && !add && _region->selection_size() > 1) {
5687 _editor->get_selection().clear_points();
5688 _region->unique_select (_primary);
5690 } else if (extend) {
5691 _region->note_selected (_primary, true, true);
5694 /* it was added during button press */
5701 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5702 _editor->commit_reversible_selection_op();
5706 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5711 NoteDrag::aborted (bool)
5716 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5717 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5718 : Drag (editor, atv->base_item ())
5720 , _y_origin (atv->y_position())
5721 , _nothing_to_drag (false)
5723 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5724 setup (atv->lines ());
5727 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5728 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5729 : Drag (editor, rv->get_canvas_group ())
5731 , _y_origin (rv->get_time_axis_view().y_position())
5732 , _nothing_to_drag (false)
5735 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5737 list<boost::shared_ptr<AutomationLine> > lines;
5739 AudioRegionView* audio_view;
5740 AutomationRegionView* automation_view;
5741 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5742 lines.push_back (audio_view->get_gain_line ());
5743 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5744 lines.push_back (automation_view->line ());
5747 error << _("Automation range drag created for invalid region type") << endmsg;
5753 /** @param lines AutomationLines to drag.
5754 * @param offset Offset from the session start to the points in the AutomationLines.
5757 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5759 /* find the lines that overlap the ranges being dragged */
5760 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5761 while (i != lines.end ()) {
5762 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5765 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5767 /* check this range against all the AudioRanges that we are using */
5768 list<AudioRange>::const_iterator k = _ranges.begin ();
5769 while (k != _ranges.end()) {
5770 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5776 /* add it to our list if it overlaps at all */
5777 if (k != _ranges.end()) {
5782 _lines.push_back (n);
5788 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5792 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5794 return 1.0 - ((global_y - _y_origin) / line->height());
5798 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5800 const double v = list->eval(x);
5801 return _integral ? rint(v) : v;
5805 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5807 Drag::start_grab (event, cursor);
5809 /* Get line states before we start changing things */
5810 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5811 i->state = &i->line->get_state ();
5812 i->original_fraction = y_fraction (i->line, current_pointer_y());
5815 if (_ranges.empty()) {
5817 /* No selected time ranges: drag all points */
5818 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5819 uint32_t const N = i->line->npoints ();
5820 for (uint32_t j = 0; j < N; ++j) {
5821 i->points.push_back (i->line->nth (j));
5827 if (_nothing_to_drag) {
5833 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5835 if (_nothing_to_drag && !first_move) {
5840 _editor->begin_reversible_command (_("automation range move"));
5842 if (!_ranges.empty()) {
5844 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5846 framecnt_t const half = (i->start + i->end) / 2;
5848 /* find the line that this audio range starts in */
5849 list<Line>::iterator j = _lines.begin();
5850 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5854 if (j != _lines.end()) {
5855 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5857 /* j is the line that this audio range starts in; fade into it;
5858 64 samples length plucked out of thin air.
5861 framepos_t a = i->start + 64;
5866 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5867 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5869 XMLNode &before = the_list->get_state();
5870 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5871 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5873 if (add_p || add_q) {
5874 _editor->session()->add_command (
5875 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5879 /* same thing for the end */
5882 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5886 if (j != _lines.end()) {
5887 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5889 /* j is the line that this audio range starts in; fade out of it;
5890 64 samples length plucked out of thin air.
5893 framepos_t b = i->end - 64;
5898 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5899 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5901 XMLNode &before = the_list->get_state();
5902 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5903 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5905 if (add_p || add_q) {
5906 _editor->session()->add_command (
5907 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5912 _nothing_to_drag = true;
5914 /* Find all the points that should be dragged and put them in the relevant
5915 points lists in the Line structs.
5918 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5920 uint32_t const N = i->line->npoints ();
5921 for (uint32_t j = 0; j < N; ++j) {
5923 /* here's a control point on this line */
5924 ControlPoint* p = i->line->nth (j);
5925 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5927 /* see if it's inside a range */
5928 list<AudioRange>::const_iterator k = _ranges.begin ();
5929 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5933 if (k != _ranges.end()) {
5934 /* dragging this point */
5935 _nothing_to_drag = false;
5936 i->points.push_back (p);
5942 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5943 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5947 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5948 float const f = y_fraction (l->line, current_pointer_y());
5949 /* we are ignoring x position for this drag, so we can just pass in anything */
5950 pair<double, float> result;
5952 result = l->line->drag_motion (0, f, true, false, ignored);
5953 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5958 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5960 if (_nothing_to_drag || !motion_occurred) {
5964 motion (event, false);
5965 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5966 i->line->end_drag (false, 0);
5969 _editor->commit_reversible_command ();
5973 AutomationRangeDrag::aborted (bool)
5975 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5980 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5982 , initial_time_axis_view (itav)
5984 /* note that time_axis_view may be null if the regionview was created
5985 * as part of a copy operation.
5987 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5988 layer = v->region()->layer ();
5989 initial_y = v->get_canvas_group()->position().y;
5990 initial_playlist = v->region()->playlist ();
5991 initial_position = v->region()->position ();
5992 initial_end = v->region()->position () + v->region()->length ();
5995 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5996 : Drag (e, i->canvas_item ())
5999 , _cumulative_dx (0)
6001 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6002 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6007 PatchChangeDrag::motion (GdkEvent* ev, bool)
6009 framepos_t f = adjusted_current_frame (ev);
6010 boost::shared_ptr<Region> r = _region_view->region ();
6011 f = max (f, r->position ());
6012 f = min (f, r->last_frame ());
6014 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6015 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6016 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6017 _cumulative_dx = dxu;
6021 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6023 if (!movement_occurred) {
6024 if (was_double_click()) {
6025 _region_view->edit_patch_change (_patch_change);
6030 boost::shared_ptr<Region> r (_region_view->region ());
6031 framepos_t f = adjusted_current_frame (ev);
6032 f = max (f, r->position ());
6033 f = min (f, r->last_frame ());
6035 _region_view->move_patch_change (
6037 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6042 PatchChangeDrag::aborted (bool)
6044 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6048 PatchChangeDrag::setup_pointer_frame_offset ()
6050 boost::shared_ptr<Region> region = _region_view->region ();
6051 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6054 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6055 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6062 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6064 _region_view->update_drag_selection (
6066 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6070 MidiRubberbandSelectDrag::deselect_things ()
6075 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6076 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6079 _vertical_only = true;
6083 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6085 double const y = _region_view->midi_view()->y_position ();
6087 y1 = max (0.0, y1 - y);
6088 y2 = max (0.0, y2 - y);
6090 _region_view->update_vertical_drag_selection (
6093 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6098 MidiVerticalSelectDrag::deselect_things ()
6103 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6104 : RubberbandSelectDrag (e, i)
6110 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6112 if (drag_in_progress) {
6113 /* We just want to select things at the end of the drag, not during it */
6117 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6119 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6121 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6123 _editor->commit_reversible_selection_op ();
6127 EditorRubberbandSelectDrag::deselect_things ()
6129 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6131 _editor->selection->clear_tracks();
6132 _editor->selection->clear_regions();
6133 _editor->selection->clear_points ();
6134 _editor->selection->clear_lines ();
6135 _editor->selection->clear_midi_notes ();
6137 _editor->commit_reversible_selection_op();
6140 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6145 _note[0] = _note[1] = 0;
6148 NoteCreateDrag::~NoteCreateDrag ()
6154 NoteCreateDrag::grid_frames (framepos_t t) const
6157 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6159 grid_beats = Evoral::Beats(1);
6162 return _region_view->region_beats_to_region_frames (grid_beats);
6166 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6168 Drag::start_grab (event, cursor);
6170 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6172 framepos_t pf = _drags->current_pointer_frame ();
6173 framecnt_t const g = grid_frames (pf);
6175 /* Hack so that we always snap to the note that we are over, instead of snapping
6176 to the next one if we're more than halfway through the one we're over.
6178 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6182 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6183 _note[1] = _note[0];
6185 MidiStreamView* sv = _region_view->midi_stream_view ();
6186 double const x = _editor->sample_to_pixel (_note[0]);
6187 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6189 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6190 _drag_rect->set_outline_all ();
6191 _drag_rect->set_outline_color (0xffffff99);
6192 _drag_rect->set_fill_color (0xffffff66);
6196 NoteCreateDrag::motion (GdkEvent* event, bool)
6198 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6199 double const x0 = _editor->sample_to_pixel (_note[0]);
6200 double const x1 = _editor->sample_to_pixel (_note[1]);
6201 _drag_rect->set_x0 (std::min(x0, x1));
6202 _drag_rect->set_x1 (std::max(x0, x1));
6206 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6208 if (!had_movement) {
6212 framepos_t const start = min (_note[0], _note[1]);
6213 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6215 framecnt_t const g = grid_frames (start);
6216 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6218 if (_editor->snap_mode() == SnapNormal && length < g) {
6222 Evoral::Beats length_beats = max (
6223 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6225 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6229 NoteCreateDrag::y_to_region (double y) const
6232 _region_view->get_canvas_group()->canvas_to_item (x, y);
6237 NoteCreateDrag::aborted (bool)
6242 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6247 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6251 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6253 Drag::start_grab (event, cursor);
6257 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6263 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6266 distance = _drags->current_pointer_x() - grab_x();
6267 len = ar->fade_in()->back()->when;
6269 distance = grab_x() - _drags->current_pointer_x();
6270 len = ar->fade_out()->back()->when;
6273 /* how long should it be ? */
6275 new_length = len + _editor->pixel_to_sample (distance);
6277 /* now check with the region that this is legal */
6279 new_length = ar->verify_xfade_bounds (new_length, start);
6282 arv->reset_fade_in_shape_width (ar, new_length);
6284 arv->reset_fade_out_shape_width (ar, new_length);
6289 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6295 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6298 distance = _drags->current_pointer_x() - grab_x();
6299 len = ar->fade_in()->back()->when;
6301 distance = grab_x() - _drags->current_pointer_x();
6302 len = ar->fade_out()->back()->when;
6305 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6307 _editor->begin_reversible_command ("xfade trim");
6308 ar->playlist()->clear_owned_changes ();
6311 ar->set_fade_in_length (new_length);
6313 ar->set_fade_out_length (new_length);
6316 /* Adjusting the xfade may affect other regions in the playlist, so we need
6317 to get undo Commands from the whole playlist rather than just the
6321 vector<Command*> cmds;
6322 ar->playlist()->rdiff (cmds);
6323 _editor->session()->add_commands (cmds);
6324 _editor->commit_reversible_command ();
6329 CrossfadeEdgeDrag::aborted (bool)
6332 // arv->redraw_start_xfade ();
6334 // arv->redraw_end_xfade ();
6338 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6339 : Drag (e, item, true)
6340 , line (new EditorCursor (*e))
6342 line->set_position (pos);
6346 RegionCutDrag::~RegionCutDrag ()
6352 RegionCutDrag::motion (GdkEvent*, bool)
6354 framepos_t where = _drags->current_pointer_frame();
6355 _editor->snap_to (where);
6357 line->set_position (where);
6361 RegionCutDrag::finished (GdkEvent*, bool)
6363 _editor->get_track_canvas()->canvas()->re_enter();
6365 framepos_t pos = _drags->current_pointer_frame();
6369 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6375 _editor->split_regions_at (pos, rs);
6379 RegionCutDrag::aborted (bool)