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 EditorOrderTimeAxisViewSorter {
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()->order_key () < rb->route()->order_key ();
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 (EditorOrderTimeAxisViewSorter ());
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, ARDOUR::Normal, 0, 1, region->name());
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>(), ARDOUR::Normal, 0, 1, region->name());
1410 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1412 rtav->set_height (original->current_height());
1417 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1423 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1425 RegionSelection new_views;
1426 PlaylistSet modified_playlists;
1427 RouteTimeAxisView* new_time_axis_view = 0;
1430 /* all changes were made during motion event handlers */
1432 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1436 _editor->commit_reversible_command ();
1440 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1441 PlaylistMapping playlist_mapping;
1443 /* insert the regions into their new playlists */
1444 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1446 RouteTimeAxisView* dest_rtv = 0;
1448 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1454 if (changed_position && !_x_constrained) {
1455 where = i->view->region()->position() - drag_delta;
1457 where = i->view->region()->position();
1460 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1461 /* dragged to drop zone */
1463 PlaylistMapping::iterator pm;
1465 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1466 /* first region from this original playlist: create a new track */
1467 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1468 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1469 dest_rtv = new_time_axis_view;
1471 /* we already created a new track for regions from this playlist, use it */
1472 dest_rtv = pm->second;
1475 /* destination time axis view is the one we dragged to */
1476 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1479 if (dest_rtv != 0) {
1480 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1481 if (new_view != 0) {
1482 new_views.push_back (new_view);
1486 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1487 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1490 list<DraggingView>::const_iterator next = i;
1496 /* If we've created new regions either by copying or moving
1497 to a new track, we want to replace the old selection with the new ones
1500 if (new_views.size() > 0) {
1501 _editor->selection->set (new_views);
1504 /* write commands for the accumulated diffs for all our modified playlists */
1505 add_stateful_diff_commands_for_playlists (modified_playlists);
1507 _editor->commit_reversible_command ();
1511 RegionMoveDrag::finished_no_copy (
1512 bool const changed_position,
1513 bool const changed_tracks,
1514 framecnt_t const drag_delta
1517 RegionSelection new_views;
1518 PlaylistSet modified_playlists;
1519 PlaylistSet frozen_playlists;
1520 set<RouteTimeAxisView*> views_to_update;
1521 RouteTimeAxisView* new_time_axis_view = 0;
1523 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1524 PlaylistMapping playlist_mapping;
1526 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1528 RegionView* rv = i->view;
1529 RouteTimeAxisView* dest_rtv = 0;
1531 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1536 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1537 /* dragged to drop zone */
1539 PlaylistMapping::iterator pm;
1541 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1542 /* first region from this original playlist: create a new track */
1543 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1544 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1545 dest_rtv = new_time_axis_view;
1547 /* we already created a new track for regions from this playlist, use it */
1548 dest_rtv = pm->second;
1552 /* destination time axis view is the one we dragged to */
1553 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1558 double const dest_layer = i->layer;
1560 views_to_update.insert (dest_rtv);
1564 if (changed_position && !_x_constrained) {
1565 where = rv->region()->position() - drag_delta;
1567 where = rv->region()->position();
1570 if (changed_tracks) {
1572 /* insert into new playlist */
1574 RegionView* new_view = insert_region_into_playlist (
1575 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1578 if (new_view == 0) {
1583 new_views.push_back (new_view);
1585 /* remove from old playlist */
1587 /* the region that used to be in the old playlist is not
1588 moved to the new one - we use a copy of it. as a result,
1589 any existing editor for the region should no longer be
1592 rv->hide_region_editor();
1595 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1599 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1601 /* this movement may result in a crossfade being modified, or a layering change,
1602 so we need to get undo data from the playlist as well as the region.
1605 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1607 playlist->clear_changes ();
1610 rv->region()->clear_changes ();
1613 motion on the same track. plonk the previously reparented region
1614 back to its original canvas group (its streamview).
1615 No need to do anything for copies as they are fake regions which will be deleted.
1618 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1619 rv->get_canvas_group()->set_y_position (i->initial_y);
1622 /* just change the model */
1623 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1624 playlist->set_layer (rv->region(), dest_layer);
1627 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1629 r = frozen_playlists.insert (playlist);
1632 playlist->freeze ();
1635 rv->region()->set_position (where);
1636 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1639 if (changed_tracks) {
1641 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1642 was selected in all of them, then removing it from a playlist will have removed all
1643 trace of it from _views (i.e. there were N regions selected, we removed 1,
1644 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1645 corresponding regionview, and _views is now empty).
1647 This could have invalidated any and all iterators into _views.
1649 The heuristic we use here is: if the region selection is empty, break out of the loop
1650 here. if the region selection is not empty, then restart the loop because we know that
1651 we must have removed at least the region(view) we've just been working on as well as any
1652 that we processed on previous iterations.
1654 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1655 we can just iterate.
1659 if (_views.empty()) {
1670 /* If we've created new regions either by copying or moving
1671 to a new track, we want to replace the old selection with the new ones
1674 if (new_views.size() > 0) {
1675 _editor->selection->set (new_views);
1678 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1682 /* write commands for the accumulated diffs for all our modified playlists */
1683 add_stateful_diff_commands_for_playlists (modified_playlists);
1684 /* applies to _brushing */
1685 _editor->commit_reversible_command ();
1687 /* We have futzed with the layering of canvas items on our streamviews.
1688 If any region changed layer, this will have resulted in the stream
1689 views being asked to set up their region views, and all will be well.
1690 If not, we might now have badly-ordered region views. Ask the StreamViews
1691 involved to sort themselves out, just in case.
1694 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1695 (*i)->view()->playlist_layered ((*i)->track ());
1699 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1700 * @param region Region to remove.
1701 * @param playlist playlist To remove from.
1702 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1703 * that clear_changes () is only called once per playlist.
1706 RegionMoveDrag::remove_region_from_playlist (
1707 boost::shared_ptr<Region> region,
1708 boost::shared_ptr<Playlist> playlist,
1709 PlaylistSet& modified_playlists
1712 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1715 playlist->clear_changes ();
1718 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1722 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1723 * clearing the playlist's diff history first if necessary.
1724 * @param region Region to insert.
1725 * @param dest_rtv Destination RouteTimeAxisView.
1726 * @param dest_layer Destination layer.
1727 * @param where Destination position.
1728 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1729 * that clear_changes () is only called once per playlist.
1730 * @return New RegionView, or 0 if no insert was performed.
1733 RegionMoveDrag::insert_region_into_playlist (
1734 boost::shared_ptr<Region> region,
1735 RouteTimeAxisView* dest_rtv,
1738 PlaylistSet& modified_playlists
1741 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1742 if (!dest_playlist) {
1746 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1747 _new_region_view = 0;
1748 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1750 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1751 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1753 dest_playlist->clear_changes ();
1756 dest_playlist->add_region (region, where);
1758 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1759 dest_playlist->set_layer (region, dest_layer);
1764 assert (_new_region_view);
1766 return _new_region_view;
1770 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1772 _new_region_view = rv;
1776 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1778 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1779 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1781 _editor->session()->add_command (c);
1790 RegionMoveDrag::aborted (bool movement_occurred)
1794 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1795 list<DraggingView>::const_iterator next = i;
1804 RegionMotionDrag::aborted (movement_occurred);
1809 RegionMotionDrag::aborted (bool)
1811 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1813 StreamView* sview = (*i)->view();
1816 if (sview->layer_display() == Expanded) {
1817 sview->set_layer_display (Stacked);
1822 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1823 RegionView* rv = i->view;
1824 TimeAxisView* tv = &(rv->get_time_axis_view ());
1825 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1827 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1828 rv->get_canvas_group()->set_y_position (0);
1830 rv->move (-_total_x_delta, 0);
1831 rv->set_height (rtv->view()->child_height ());
1835 /** @param b true to brush, otherwise false.
1836 * @param c true to make copies of the regions being moved, otherwise false.
1838 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1839 : RegionMotionDrag (e, i, p, v, b)
1841 , _new_region_view (0)
1843 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1846 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1847 if (rtv && rtv->is_track()) {
1848 speed = rtv->track()->speed ();
1851 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1855 RegionMoveDrag::setup_pointer_frame_offset ()
1857 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1860 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1861 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1863 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1865 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1866 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1868 _primary = v->view()->create_region_view (r, false, false);
1870 _primary->get_canvas_group()->show ();
1871 _primary->set_position (pos, 0);
1872 _views.push_back (DraggingView (_primary, this, v));
1874 _last_frame_position = pos;
1876 _item = _primary->get_canvas_group ();
1880 RegionInsertDrag::finished (GdkEvent *, bool)
1882 int pos = _views.front().time_axis_view;
1883 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1885 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1887 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1888 _primary->get_canvas_group()->set_y_position (0);
1890 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1892 _editor->begin_reversible_command (Operations::insert_region);
1893 playlist->clear_changes ();
1894 playlist->add_region (_primary->region (), _last_frame_position);
1896 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1897 if (Config->get_edit_mode() == Ripple) {
1898 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1901 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1902 _editor->commit_reversible_command ();
1910 RegionInsertDrag::aborted (bool)
1917 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1918 : RegionMoveDrag (e, i, p, v, false, false)
1920 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1923 struct RegionSelectionByPosition {
1924 bool operator() (RegionView*a, RegionView* b) {
1925 return a->region()->position () < b->region()->position();
1930 RegionSpliceDrag::motion (GdkEvent* event, bool)
1932 /* Which trackview is this ? */
1934 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1935 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1937 /* The region motion is only processed if the pointer is over
1941 if (!tv || !tv->is_track()) {
1942 /* To make sure we hide the verbose canvas cursor when the mouse is
1943 not held over an audio track.
1945 _editor->verbose_cursor()->hide ();
1948 _editor->verbose_cursor()->show ();
1953 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1959 RegionSelection copy;
1960 _editor->selection->regions.by_position(copy);
1962 framepos_t const pf = adjusted_current_frame (event);
1964 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1966 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1972 boost::shared_ptr<Playlist> playlist;
1974 if ((playlist = atv->playlist()) == 0) {
1978 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1983 if (pf < (*i)->region()->last_frame() + 1) {
1987 if (pf > (*i)->region()->first_frame()) {
1993 playlist->shuffle ((*i)->region(), dir);
1998 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2000 RegionMoveDrag::finished (event, movement_occurred);
2004 RegionSpliceDrag::aborted (bool)
2014 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2017 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2019 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2020 RegionSelection to_ripple;
2021 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2022 if ((*i)->position() >= where) {
2023 to_ripple.push_back (rtv->view()->find_view(*i));
2027 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2028 if (!exclude.contains (*i)) {
2029 // the selection has already been added to _views
2031 if (drag_in_progress) {
2032 // do the same things that RegionMotionDrag::motion does when
2033 // first_move is true, for the region views that we're adding
2034 // to _views this time
2037 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2038 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2039 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2040 rvg->reparent (_editor->_drag_motion_group);
2042 // we only need to move in the y direction
2043 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2048 _views.push_back (DraggingView (*i, this, tav));
2054 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2057 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2058 // we added all the regions after the selection
2060 std::list<DraggingView>::iterator to_erase = i++;
2061 if (!_editor->selection->regions.contains (to_erase->view)) {
2062 // restore the non-selected regions to their original playlist & positions,
2063 // and then ripple them back by the length of the regions that were dragged away
2064 // do the same things as RegionMotionDrag::aborted
2066 RegionView *rv = to_erase->view;
2067 TimeAxisView* tv = &(rv->get_time_axis_view ());
2068 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2071 // plonk them back onto their own track
2072 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2073 rv->get_canvas_group()->set_y_position (0);
2077 // move the underlying region to match the view
2078 rv->region()->set_position (rv->region()->position() + amount);
2080 // restore the view to match the underlying region's original position
2081 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2084 rv->set_height (rtv->view()->child_height ());
2085 _views.erase (to_erase);
2091 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2093 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2095 return allow_moves_across_tracks;
2103 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2104 : RegionMoveDrag (e, i, p, v, false, false)
2106 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2107 // compute length of selection
2108 RegionSelection selected_regions = _editor->selection->regions;
2109 selection_length = selected_regions.end_frame() - selected_regions.start();
2111 // we'll only allow dragging to another track in ripple mode if all the regions
2112 // being dragged start off on the same track
2113 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2116 exclude = new RegionList;
2117 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2118 exclude->push_back((*i)->region());
2121 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2122 RegionSelection copy;
2123 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2125 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2126 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2128 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2129 // find ripple start point on each applicable playlist
2130 RegionView *first_selected_on_this_track = NULL;
2131 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2132 if ((*i)->region()->playlist() == (*pi)) {
2133 // region is on this playlist - it's the first, because they're sorted
2134 first_selected_on_this_track = *i;
2138 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2139 add_all_after_to_views (
2140 &first_selected_on_this_track->get_time_axis_view(),
2141 first_selected_on_this_track->region()->position(),
2142 selected_regions, false);
2145 if (allow_moves_across_tracks) {
2146 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2154 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2156 /* Which trackview is this ? */
2158 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2159 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2161 /* The region motion is only processed if the pointer is over
2165 if (!tv || !tv->is_track()) {
2166 /* To make sure we hide the verbose canvas cursor when the mouse is
2167 not held over an audiotrack.
2169 _editor->verbose_cursor()->hide ();
2173 framepos_t where = adjusted_current_frame (event);
2174 assert (where >= 0);
2176 double delta = compute_x_delta (event, &after);
2178 framecnt_t amount = _editor->pixel_to_sample (delta);
2180 if (allow_moves_across_tracks) {
2181 // all the originally selected regions were on the same track
2183 framecnt_t adjust = 0;
2184 if (prev_tav && tv != prev_tav) {
2185 // dragged onto a different track
2186 // remove the unselected regions from _views, restore them to their original positions
2187 // and add the regions after the drop point on the new playlist to _views instead.
2188 // undo the effect of rippling the previous playlist, and include the effect of removing
2189 // the dragged region(s) from this track
2191 remove_unselected_from_views (prev_amount, false);
2192 // ripple previous playlist according to the regions that have been removed onto the new playlist
2193 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2196 // move just the selected regions
2197 RegionMoveDrag::motion(event, first_move);
2199 // ensure that the ripple operation on the new playlist inserts selection_length time
2200 adjust = selection_length;
2201 // ripple the new current playlist
2202 tv->playlist()->ripple (where, amount+adjust, exclude);
2204 // add regions after point where drag entered this track to subsequent ripples
2205 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2208 // motion on same track
2209 RegionMoveDrag::motion(event, first_move);
2213 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2214 prev_position = where;
2216 // selection encompasses multiple tracks - just drag
2217 // cross-track drags are forbidden
2218 RegionMoveDrag::motion(event, first_move);
2221 if (!_x_constrained) {
2222 prev_amount += amount;
2225 _last_frame_position = after;
2229 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2231 if (!movement_occurred) {
2235 if (was_double_click() && !_views.empty()) {
2236 DraggingView dv = _views.front();
2237 dv.view->show_region_editor ();
2244 _editor->begin_reversible_command(_("Ripple drag"));
2246 // remove the regions being rippled from the dragging view, updating them to
2247 // their new positions
2248 remove_unselected_from_views (prev_amount, true);
2250 if (allow_moves_across_tracks) {
2252 // if regions were dragged across tracks, we've rippled any later
2253 // regions on the track the regions were dragged off, so we need
2254 // to add the original track to the undo record
2255 orig_tav->playlist()->clear_changes();
2256 vector<Command*> cmds;
2257 orig_tav->playlist()->rdiff (cmds);
2258 _editor->session()->add_commands (cmds);
2260 if (prev_tav && prev_tav != orig_tav) {
2261 prev_tav->playlist()->clear_changes();
2262 vector<Command*> cmds;
2263 prev_tav->playlist()->rdiff (cmds);
2264 _editor->session()->add_commands (cmds);
2267 // selection spanned multiple tracks - all will need adding to undo record
2269 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2270 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2272 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2273 (*pi)->clear_changes();
2274 vector<Command*> cmds;
2275 (*pi)->rdiff (cmds);
2276 _editor->session()->add_commands (cmds);
2280 // other modified playlists are added to undo by RegionMoveDrag::finished()
2281 RegionMoveDrag::finished (event, movement_occurred);
2282 _editor->commit_reversible_command();
2286 RegionRippleDrag::aborted (bool movement_occurred)
2288 RegionMoveDrag::aborted (movement_occurred);
2293 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2295 _view (dynamic_cast<MidiTimeAxisView*> (v))
2297 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2303 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2306 _editor->begin_reversible_command (_("create region"));
2307 _region = add_midi_region (_view, false);
2308 _view->playlist()->freeze ();
2311 framepos_t const f = adjusted_current_frame (event);
2312 if (f < grab_frame()) {
2313 _region->set_initial_position (f);
2316 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2317 so that if this region is duplicated, its duplicate starts on
2318 a snap point rather than 1 frame after a snap point. Otherwise things get
2319 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2320 place snapped notes at the start of the region.
2323 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2324 _region->set_length (len < 1 ? 1 : len);
2330 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2332 if (!movement_occurred) {
2333 add_midi_region (_view, true);
2335 _view->playlist()->thaw ();
2336 _editor->commit_reversible_command();
2341 RegionCreateDrag::aborted (bool)
2344 _view->playlist()->thaw ();
2350 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2355 , _was_selected (false)
2358 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2362 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2364 Gdk::Cursor* cursor;
2365 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2367 float x_fraction = cnote->mouse_x_fraction ();
2369 if (x_fraction > 0.0 && x_fraction < 0.25) {
2370 cursor = _editor->cursors()->left_side_trim;
2373 cursor = _editor->cursors()->right_side_trim;
2377 Drag::start_grab (event, cursor);
2379 region = &cnote->region_view();
2382 temp = region->snap_to_pixel (cnote->x0 (), true);
2383 _snap_delta = temp - cnote->x0 ();
2387 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2392 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2393 if (ms.size() > 1) {
2394 /* has to be relative, may make no sense otherwise */
2398 if (!(_was_selected = cnote->selected())) {
2400 /* tertiary-click means extend selection - we'll do that on button release,
2401 so don't add it here, because otherwise we make it hard to figure
2402 out the "extend-to" range.
2405 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2408 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2411 region->note_selected (cnote, true);
2413 _editor->get_selection().clear_points();
2414 region->unique_select (cnote);
2421 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2423 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2425 _editor->begin_reversible_command (_("resize notes"));
2427 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2428 MidiRegionSelection::iterator next;
2431 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2433 mrv->begin_resizing (at_front);
2439 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2440 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2442 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2446 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2448 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2449 if (_editor->snap_mode () != SnapOff) {
2453 if (_editor->snap_mode () == SnapOff) {
2455 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2456 if (apply_snap_delta) {
2462 if (apply_snap_delta) {
2466 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2472 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2474 if (!movement_occurred) {
2475 /* no motion - select note */
2476 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2477 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2478 _editor->current_mouse_mode() == Editing::MouseDraw) {
2480 bool changed = false;
2482 if (_was_selected) {
2483 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2485 region->note_deselected (cnote);
2488 _editor->get_selection().clear_points();
2489 region->unique_select (cnote);
2493 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2494 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2496 if (!extend && !add && region->selection_size() > 1) {
2497 _editor->get_selection().clear_points();
2498 region->unique_select (cnote);
2500 } else if (extend) {
2501 region->note_selected (cnote, true, true);
2504 /* it was added during button press */
2510 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2511 _editor->commit_reversible_selection_op();
2518 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2519 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2520 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2522 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2525 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2527 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2528 if (_editor->snap_mode () != SnapOff) {
2532 if (_editor->snap_mode () == SnapOff) {
2534 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2535 if (apply_snap_delta) {
2541 if (apply_snap_delta) {
2545 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2549 _editor->commit_reversible_command ();
2553 NoteResizeDrag::aborted (bool)
2555 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2556 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2557 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2559 mrv->abort_resizing ();
2564 AVDraggingView::AVDraggingView (RegionView* v)
2567 initial_position = v->region()->position ();
2570 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2573 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2576 TrackViewList empty;
2578 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2579 std::list<RegionView*> views = rs.by_layer();
2582 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2583 RegionView* rv = (*i);
2584 if (!rv->region()->video_locked()) {
2587 if (rv->region()->locked()) {
2590 _views.push_back (AVDraggingView (rv));
2595 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2597 Drag::start_grab (event);
2598 if (_editor->session() == 0) {
2602 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2608 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2612 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2613 _max_backwards_drag = (
2614 ARDOUR_UI::instance()->video_timeline->get_duration()
2615 + ARDOUR_UI::instance()->video_timeline->get_offset()
2616 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2619 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2620 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2621 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2624 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2627 Timecode::Time timecode;
2628 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2629 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);
2630 show_verbose_cursor_text (buf);
2634 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2636 if (_editor->session() == 0) {
2639 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2643 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2647 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2648 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2650 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2651 dt = - _max_backwards_drag;
2654 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2655 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2657 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2658 RegionView* rv = i->view;
2659 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2662 rv->region()->clear_changes ();
2663 rv->region()->suspend_property_changes();
2665 rv->region()->set_position(i->initial_position + dt);
2666 rv->region_changed(ARDOUR::Properties::position);
2669 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2670 Timecode::Time timecode;
2671 Timecode::Time timediff;
2673 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2674 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2675 snprintf (buf, sizeof (buf),
2676 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2677 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2678 , _("Video Start:"),
2679 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2681 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2683 show_verbose_cursor_text (buf);
2687 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2689 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2696 if (!movement_occurred || ! _editor->session()) {
2700 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2702 _editor->begin_reversible_command (_("Move Video"));
2704 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2705 ARDOUR_UI::instance()->video_timeline->save_undo();
2706 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2707 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2709 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2710 i->view->drag_end();
2711 i->view->region()->resume_property_changes ();
2713 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2716 _editor->session()->maybe_update_session_range(
2717 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2718 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2722 _editor->commit_reversible_command ();
2726 VideoTimeLineDrag::aborted (bool)
2728 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2731 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2732 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2734 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2735 i->view->region()->resume_property_changes ();
2736 i->view->region()->set_position(i->initial_position);
2740 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2741 : RegionDrag (e, i, p, v)
2742 , _operation (StartTrim)
2743 , _preserve_fade_anchor (preserve_fade_anchor)
2744 , _jump_position_when_done (false)
2746 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2750 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2753 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2754 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2756 if (tv && tv->is_track()) {
2757 speed = tv->track()->speed();
2760 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2761 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2762 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2764 framepos_t const pf = adjusted_current_frame (event);
2765 setup_snap_delta (region_start);
2767 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2768 /* Move the contents of the region around without changing the region bounds */
2769 _operation = ContentsTrim;
2770 Drag::start_grab (event, _editor->cursors()->trimmer);
2772 /* These will get overridden for a point trim.*/
2773 if (pf < (region_start + region_length/2)) {
2774 /* closer to front */
2775 _operation = StartTrim;
2776 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2777 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2779 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2783 _operation = EndTrim;
2784 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2785 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2787 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2791 /* jump trim disabled for now
2792 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2793 _jump_position_when_done = true;
2797 switch (_operation) {
2799 show_verbose_cursor_time (region_start);
2802 show_verbose_cursor_duration (region_start, region_end);
2805 show_verbose_cursor_time (pf);
2809 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2810 i->view->region()->suspend_property_changes ();
2815 TrimDrag::motion (GdkEvent* event, bool first_move)
2817 RegionView* rv = _primary;
2820 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2821 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2822 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2823 frameoffset_t frame_delta = 0;
2825 if (tv && tv->is_track()) {
2826 speed = tv->track()->speed();
2828 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2829 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2835 switch (_operation) {
2837 trim_type = "Region start trim";
2840 trim_type = "Region end trim";
2843 trim_type = "Region content trim";
2850 _editor->begin_reversible_command (trim_type);
2852 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2853 RegionView* rv = i->view;
2854 rv->enable_display (false);
2855 rv->region()->playlist()->clear_owned_changes ();
2857 if (_operation == StartTrim) {
2858 rv->trim_front_starting ();
2861 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2864 arv->temporarily_hide_envelope ();
2868 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2869 insert_result = _editor->motion_frozen_playlists.insert (pl);
2871 if (insert_result.second) {
2877 bool non_overlap_trim = false;
2879 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2880 non_overlap_trim = true;
2883 /* contstrain trim to fade length */
2884 if (_preserve_fade_anchor) {
2885 switch (_operation) {
2887 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2888 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2890 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2891 if (ar->locked()) continue;
2892 framecnt_t len = ar->fade_in()->back()->when;
2893 if (len < dt) dt = min(dt, len);
2897 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2898 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2900 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2901 if (ar->locked()) continue;
2902 framecnt_t len = ar->fade_out()->back()->when;
2903 if (len < -dt) dt = max(dt, -len);
2912 switch (_operation) {
2914 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2915 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2916 if (changed && _preserve_fade_anchor) {
2917 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2919 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2920 framecnt_t len = ar->fade_in()->back()->when;
2921 framecnt_t diff = ar->first_frame() - i->initial_position;
2922 framepos_t new_length = len - diff;
2923 i->anchored_fade_length = min (ar->length(), new_length);
2924 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2925 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2932 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2933 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2934 if (changed && _preserve_fade_anchor) {
2935 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2937 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2938 framecnt_t len = ar->fade_out()->back()->when;
2939 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2940 framepos_t new_length = len + diff;
2941 i->anchored_fade_length = min (ar->length(), new_length);
2942 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2943 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2951 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2953 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2954 i->view->move_contents (frame_delta);
2960 switch (_operation) {
2962 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2965 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2968 // show_verbose_cursor_time (frame_delta);
2974 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2976 if (movement_occurred) {
2977 motion (event, false);
2979 if (_operation == StartTrim) {
2980 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2982 /* This must happen before the region's StatefulDiffCommand is created, as it may
2983 `correct' (ahem) the region's _start from being negative to being zero. It
2984 needs to be zero in the undo record.
2986 i->view->trim_front_ending ();
2988 if (_preserve_fade_anchor) {
2989 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2991 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2992 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2993 ar->set_fade_in_length(i->anchored_fade_length);
2994 ar->set_fade_in_active(true);
2997 if (_jump_position_when_done) {
2998 i->view->region()->set_position (i->initial_position);
3001 } else if (_operation == EndTrim) {
3002 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3003 if (_preserve_fade_anchor) {
3004 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3006 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3007 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3008 ar->set_fade_out_length(i->anchored_fade_length);
3009 ar->set_fade_out_active(true);
3012 if (_jump_position_when_done) {
3013 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3018 if (!_views.empty()) {
3019 if (_operation == StartTrim) {
3020 _editor->maybe_locate_with_edit_preroll(
3021 _views.begin()->view->region()->position());
3023 if (_operation == EndTrim) {
3024 _editor->maybe_locate_with_edit_preroll(
3025 _views.begin()->view->region()->position() +
3026 _views.begin()->view->region()->length());
3030 if (!_editor->selection->selected (_primary)) {
3031 _primary->thaw_after_trim ();
3034 set<boost::shared_ptr<Playlist> > diffed_playlists;
3036 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3037 i->view->thaw_after_trim ();
3038 i->view->enable_display (true);
3040 /* Trimming one region may affect others on the playlist, so we need
3041 to get undo Commands from the whole playlist rather than just the
3042 region. Use diffed_playlists to make sure we don't diff a given
3043 playlist more than once.
3045 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3046 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3047 vector<Command*> cmds;
3049 _editor->session()->add_commands (cmds);
3050 diffed_playlists.insert (p);
3055 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3059 _editor->motion_frozen_playlists.clear ();
3060 _editor->commit_reversible_command();
3063 /* no mouse movement */
3064 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3065 _editor->point_trim (event, adjusted_current_frame (event));
3069 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3070 i->view->region()->resume_property_changes ();
3075 TrimDrag::aborted (bool movement_occurred)
3077 /* Our motion method is changing model state, so use the Undo system
3078 to cancel. Perhaps not ideal, as this will leave an Undo point
3079 behind which may be slightly odd from the user's point of view.
3084 if (movement_occurred) {
3088 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3089 i->view->region()->resume_property_changes ();
3094 TrimDrag::setup_pointer_frame_offset ()
3096 list<DraggingView>::iterator i = _views.begin ();
3097 while (i != _views.end() && i->view != _primary) {
3101 if (i == _views.end()) {
3105 switch (_operation) {
3107 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3110 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3117 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3122 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3123 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3125 _real_section = &_marker->meter();
3130 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3132 Drag::start_grab (event, cursor);
3133 show_verbose_cursor_time (adjusted_current_frame(event));
3137 MeterMarkerDrag::setup_pointer_frame_offset ()
3139 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3143 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3145 if (!_marker->meter().movable()) {
3151 // create a dummy marker to catch events, then hide it.
3154 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3156 _marker = new MeterMarker (
3158 *_editor->meter_group,
3159 UIConfiguration::instance().color ("meter marker"),
3161 *new MeterSection (_marker->meter())
3164 /* use the new marker for the grab */
3165 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3168 TempoMap& map (_editor->session()->tempo_map());
3169 /* get current state */
3170 before_state = &map.get_state();
3173 _editor->begin_reversible_command (_("move meter mark"));
3175 _editor->begin_reversible_command (_("copy meter mark"));
3177 Timecode::BBT_Time bbt = _real_section->bbt();
3179 /* we can't add a meter where one currently exists */
3180 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3185 const double beat = map.bbt_to_beats (bbt);
3187 if (_real_section->position_lock_style() == AudioTime) {
3188 _real_section = map.add_meter_frame (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3189 , map.frame_time (bbt), beat, bbt);
3191 _real_section = map.add_meter_beat (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor()), beat, bbt);
3196 framepos_t const pf = adjusted_current_frame (event, false);
3197 if (_marker->meter().position_lock_style() == MusicTime) {
3198 TempoMap& map (_editor->session()->tempo_map());
3199 Timecode::BBT_Time bbt;
3200 map.bbt_time (pf, bbt);
3201 /* round bbt to bars */
3202 map.round_bbt (bbt, -1, RoundNearest);
3204 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3205 /* adjust previous tempo to match meter frame */
3206 _editor->session()->tempo_map().gui_dilate_tempo (_real_section, pf);
3207 } else if ((bbt.bars != _real_section->bbt().bars && pf > last_pointer_frame())
3208 || (bbt.bars < _real_section->bbt().bars && pf < last_pointer_frame())) {
3209 /* move meter beat-based */
3210 _editor->session()->tempo_map().gui_move_meter_bbt (_real_section, bbt);
3214 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3215 /* currently disabled in the lib for AudioTime */
3216 if (_real_section->movable()) {
3217 _editor->session()->tempo_map().gui_dilate_tempo (_real_section, pf);
3220 _editor->session()->tempo_map().gui_move_meter_frame (_real_section, pf);
3223 _marker->set_position (pf);
3224 show_verbose_cursor_time (_real_section->frame());
3228 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3230 if (!movement_occurred) {
3231 if (was_double_click()) {
3232 _editor->edit_meter_marker (*_marker);
3237 TempoMap& map (_editor->session()->tempo_map());
3239 XMLNode &after = map.get_state();
3240 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3241 _editor->commit_reversible_command ();
3243 // delete the dummy marker we used for visual representation while moving.
3244 // a new visual marker will show up automatically.
3249 MeterMarkerDrag::aborted (bool moved)
3251 _marker->set_position (_marker->meter().frame ());
3253 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3254 // delete the dummy marker we used for visual representation while moving.
3255 // a new visual marker will show up automatically.
3260 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3265 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3267 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3268 _real_section = &_marker->tempo();
3269 _movable = _real_section->movable();
3274 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3276 Drag::start_grab (event, cursor);
3277 if (!_real_section->active()) {
3278 show_verbose_cursor_text (_("inactive"));
3280 show_verbose_cursor_time (adjusted_current_frame (event));
3285 TempoMarkerDrag::setup_pointer_frame_offset ()
3287 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3291 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3293 if (!_real_section->active()) {
3298 // mvc drag - create a dummy marker to catch events, hide it.
3301 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3303 TempoSection section (_marker->tempo());
3305 _marker = new TempoMarker (
3307 *_editor->tempo_group,
3308 UIConfiguration::instance().color ("tempo marker"),
3310 *new TempoSection (_marker->tempo())
3313 /* use the new marker for the grab */
3314 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3317 TempoMap& map (_editor->session()->tempo_map());
3318 /* get current state */
3319 before_state = &map.get_state();
3321 _editor->begin_reversible_command (_("move tempo mark"));
3323 _editor->begin_reversible_command (_("copy tempo mark"));
3325 bool use_snap = false;
3327 if (!_editor->snap_musical()) {
3328 frame = adjusted_current_frame (event);
3330 frame = adjusted_current_frame (event, false);
3331 if (ArdourKeyboard::indicates_snap (event->button.state)) {
3332 if (_editor->snap_mode() == Editing::SnapOff) {
3338 if (_editor->snap_mode() == Editing::SnapOff) {
3346 Timecode::BBT_Time bbt;
3347 map.bbt_time (frame, bbt);
3349 /* add new tempo section to map, ensuring we don't refer to existing tempos for snap */
3351 if (_real_section->position_lock_style() == MusicTime) {
3352 if (use_snap && _editor->snap_type() == SnapToBar) {
3353 map.round_bbt (bbt, -1, (frame > _real_section->frame()) ? RoundUpMaybe : RoundDownMaybe);
3354 } else if (use_snap) {
3355 map.round_bbt (bbt, _editor->get_grid_beat_divisions (0), RoundNearest);
3357 double const pulse = map.predict_tempo_pulse (_real_section, map.frame_time (bbt));
3358 _real_section = map.add_tempo_pulse (_marker->tempo(), pulse, _real_section->type());
3360 if (use_snap && _editor->snap_type() == SnapToBar) {
3361 map.round_bbt (bbt, -1, (frame > _real_section->frame()) ? RoundUpMaybe : RoundDownMaybe);
3362 } else if (use_snap) {
3363 map.round_bbt (bbt, _editor->get_grid_beat_divisions (0), RoundNearest);
3366 frame = map.predict_tempo_frame (_real_section, bbt);
3368 _real_section = map.add_tempo_frame (_marker->tempo(), frame, _real_section->type());
3376 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3377 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3378 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3381 show_verbose_cursor_text (strs.str());
3382 } else if (_movable && !_real_section->locked_to_meter()) {
3383 if (!_editor->snap_musical()) {
3384 /* snap normally (this is not self-referential).*/
3385 pf = adjusted_current_frame (event);
3388 we can't use the map for anything related to tempo,
3389 so we round bbt using meters, which have no dependency
3390 on pulse for this kind of thing.
3393 TempoMap& map (_editor->session()->tempo_map());
3395 pf = adjusted_current_frame (event, false);
3396 if (ArdourKeyboard::indicates_snap (event->button.state)) {
3397 if (_editor->snap_mode() == Editing::SnapOff) {
3403 if (_editor->snap_mode() == Editing::SnapOff) {
3410 Timecode::BBT_Time when;
3411 map.bbt_time (pf, when);
3413 if (_real_section->position_lock_style() == MusicTime) {
3415 const double pulse = map.predict_tempo_pulse (_real_section, pf);
3416 when = map.pulse_to_bbt (pulse);
3417 if (use_snap && _editor->snap_type() == SnapToBar) {
3418 map.round_bbt (when, -1, (pf > _real_section->frame()) ? RoundUpMaybe : RoundDownMaybe);
3419 } else if (use_snap) {
3420 map.round_bbt (when, _editor->get_grid_beat_divisions (0), RoundNearest);
3422 const double beat = map.bbt_to_beats (when);
3423 map.gui_move_tempo_beat (_real_section, beat);
3425 if (use_snap && _editor->snap_type() == SnapToBar) {
3426 map.round_bbt (when, -1, (pf > _real_section->frame()) ? RoundUpMaybe : RoundDownMaybe);
3427 } else if (use_snap) {
3428 map.round_bbt (when, _editor->get_grid_beat_divisions (0), RoundNearest);
3431 pf = map.predict_tempo_frame (_real_section, when);
3433 map.gui_move_tempo_frame (_real_section, pf);
3437 show_verbose_cursor_time (_real_section->frame());
3439 _marker->set_position (pf);
3443 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3445 if (!_real_section->active()) {
3448 if (!movement_occurred) {
3449 if (was_double_click()) {
3450 _editor->edit_tempo_marker (*_marker);
3455 TempoMap& map (_editor->session()->tempo_map());
3457 XMLNode &after = map.get_state();
3458 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3459 _editor->commit_reversible_command ();
3461 // delete the dummy marker we used for visual representation while moving.
3462 // a new visual marker will show up automatically.
3467 TempoMarkerDrag::aborted (bool moved)
3469 _marker->set_position (_marker->tempo().frame());
3471 TempoMap& map (_editor->session()->tempo_map());
3472 map.set_state (*before_state, Stateful::current_state_version);
3473 // delete the dummy marker we used for visual representation while moving.
3474 // a new visual marker will show up automatically.
3479 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3486 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3491 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3493 Drag::start_grab (event, cursor);
3495 TempoMap& map (_editor->session()->tempo_map());
3498 _tempo = const_cast<TempoSection*> (&map.tempo_section_at (raw_grab_frame()));
3499 sstr << "^" << fixed << setprecision(3) << map.tempo_at (adjusted_current_frame (event)).beats_per_minute() << "\n";
3500 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3501 show_verbose_cursor_text (sstr.str());
3502 finished (event, false);
3506 BBTRulerDrag::setup_pointer_frame_offset ()
3508 TempoMap& map (_editor->session()->tempo_map());
3509 const double beat_at_frame = map.beat_at_frame (raw_grab_frame());
3510 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3511 if (divisions > 0) {
3512 _beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3514 /* while it makes some sense for the user to determine the division to 'grab',
3515 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3516 and the result over steep tempo curves. Use sixteenths.
3518 _beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3520 _pulse = map.pulse_at_beat (_beat);
3521 _pointer_frame_offset = raw_grab_frame() - map.frame_at_beat (_beat);
3525 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3527 TempoMap& map (_editor->session()->tempo_map());
3530 /* get current state */
3531 before_state = &map.get_state();
3532 _editor->begin_reversible_command (_("dilate tempo"));
3535 framepos_t const pf = adjusted_current_frame (event, false);
3537 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3538 /* adjust previous tempo to match pointer frame */
3539 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3542 sstr << "^" << fixed << setprecision(3) << map.tempo_at (pf).beats_per_minute() << "\n";
3543 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3544 show_verbose_cursor_text (sstr.str());
3548 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3550 if (!movement_occurred) {
3554 TempoMap& map (_editor->session()->tempo_map());
3556 XMLNode &after = map.get_state();
3557 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3558 _editor->commit_reversible_command ();
3562 BBTRulerDrag::aborted (bool moved)
3565 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3570 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3571 : Drag (e, &c.track_canvas_item(), false)
3576 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3579 /** Do all the things we do when dragging the playhead to make it look as though
3580 * we have located, without actually doing the locate (because that would cause
3581 * the diskstream buffers to be refilled, which is too slow).
3584 CursorDrag::fake_locate (framepos_t t)
3586 if (_editor->session () == 0) {
3590 _editor->playhead_cursor->set_position (t);
3592 Session* s = _editor->session ();
3593 if (s->timecode_transmission_suspended ()) {
3594 framepos_t const f = _editor->playhead_cursor->current_frame ();
3595 /* This is asynchronous so it will be sent "now"
3597 s->send_mmc_locate (f);
3598 /* These are synchronous and will be sent during the next
3601 s->queue_full_time_code ();
3602 s->queue_song_position_pointer ();
3605 show_verbose_cursor_time (t);
3606 _editor->UpdateAllTransportClocks (t);
3610 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3612 Drag::start_grab (event, c);
3613 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3615 _grab_zoom = _editor->samples_per_pixel;
3617 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3619 _editor->snap_to_with_modifier (where, event);
3621 _editor->_dragging_playhead = true;
3623 Session* s = _editor->session ();
3625 /* grab the track canvas item as well */
3627 _cursor.track_canvas_item().grab();
3630 if (_was_rolling && _stop) {
3634 if (s->is_auditioning()) {
3635 s->cancel_audition ();
3639 if (AudioEngine::instance()->connected()) {
3641 /* do this only if we're the engine is connected
3642 * because otherwise this request will never be
3643 * serviced and we'll busy wait forever. likewise,
3644 * notice if we are disconnected while waiting for the
3645 * request to be serviced.
3648 s->request_suspend_timecode_transmission ();
3649 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3650 /* twiddle our thumbs */
3655 fake_locate (where - snap_delta (event->button.state));
3659 CursorDrag::motion (GdkEvent* event, bool)
3661 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3662 _editor->snap_to_with_modifier (where, event);
3663 if (where != last_pointer_frame()) {
3664 fake_locate (where - snap_delta (event->button.state));
3669 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3671 _editor->_dragging_playhead = false;
3673 _cursor.track_canvas_item().ungrab();
3675 if (!movement_occurred && _stop) {
3679 motion (event, false);
3681 Session* s = _editor->session ();
3683 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3684 _editor->_pending_locate_request = true;
3685 s->request_resume_timecode_transmission ();
3690 CursorDrag::aborted (bool)
3692 _cursor.track_canvas_item().ungrab();
3694 if (_editor->_dragging_playhead) {
3695 _editor->session()->request_resume_timecode_transmission ();
3696 _editor->_dragging_playhead = false;
3699 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3702 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3703 : RegionDrag (e, i, p, v)
3705 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3709 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3711 Drag::start_grab (event, cursor);
3713 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3714 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3715 setup_snap_delta (r->position ());
3717 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3721 FadeInDrag::setup_pointer_frame_offset ()
3723 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3724 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3725 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3729 FadeInDrag::motion (GdkEvent* event, bool)
3731 framecnt_t fade_length;
3733 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3734 _editor->snap_to_with_modifier (pos, event);
3735 pos -= snap_delta (event->button.state);
3737 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3739 if (pos < (region->position() + 64)) {
3740 fade_length = 64; // this should be a minimum defined somewhere
3741 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3742 fade_length = region->length() - region->fade_out()->back()->when - 1;
3744 fade_length = pos - region->position();
3747 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3749 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3755 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3758 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3762 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3764 if (!movement_occurred) {
3768 framecnt_t fade_length;
3769 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3770 _editor->snap_to_with_modifier (pos, event);
3771 pos -= snap_delta (event->button.state);
3773 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3775 if (pos < (region->position() + 64)) {
3776 fade_length = 64; // this should be a minimum defined somewhere
3777 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3778 fade_length = region->length() - region->fade_out()->back()->when - 1;
3780 fade_length = pos - region->position();
3783 bool in_command = false;
3785 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3787 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3793 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3794 XMLNode &before = alist->get_state();
3796 tmp->audio_region()->set_fade_in_length (fade_length);
3797 tmp->audio_region()->set_fade_in_active (true);
3800 _editor->begin_reversible_command (_("change fade in length"));
3803 XMLNode &after = alist->get_state();
3804 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3808 _editor->commit_reversible_command ();
3813 FadeInDrag::aborted (bool)
3815 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3816 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3822 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3826 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3827 : RegionDrag (e, i, p, v)
3829 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3833 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3835 Drag::start_grab (event, cursor);
3837 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3838 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3839 setup_snap_delta (r->last_frame ());
3841 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3845 FadeOutDrag::setup_pointer_frame_offset ()
3847 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3848 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3849 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3853 FadeOutDrag::motion (GdkEvent* event, bool)
3855 framecnt_t fade_length;
3857 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3858 _editor->snap_to_with_modifier (pos, event);
3859 pos -= snap_delta (event->button.state);
3861 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3863 if (pos > (region->last_frame() - 64)) {
3864 fade_length = 64; // this should really be a minimum fade defined somewhere
3865 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3866 fade_length = region->length() - region->fade_in()->back()->when - 1;
3868 fade_length = region->last_frame() - pos;
3871 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3873 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3879 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3882 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3886 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3888 if (!movement_occurred) {
3892 framecnt_t fade_length;
3894 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3895 _editor->snap_to_with_modifier (pos, event);
3896 pos -= snap_delta (event->button.state);
3898 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3900 if (pos > (region->last_frame() - 64)) {
3901 fade_length = 64; // this should really be a minimum fade defined somewhere
3902 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3903 fade_length = region->length() - region->fade_in()->back()->when - 1;
3905 fade_length = region->last_frame() - pos;
3908 bool in_command = false;
3910 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3912 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3918 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3919 XMLNode &before = alist->get_state();
3921 tmp->audio_region()->set_fade_out_length (fade_length);
3922 tmp->audio_region()->set_fade_out_active (true);
3925 _editor->begin_reversible_command (_("change fade out length"));
3928 XMLNode &after = alist->get_state();
3929 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3933 _editor->commit_reversible_command ();
3938 FadeOutDrag::aborted (bool)
3940 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3941 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3947 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3951 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3953 , _selection_changed (false)
3955 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3956 Gtk::Window* toplevel = _editor->current_toplevel();
3957 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3961 _points.push_back (ArdourCanvas::Duple (0, 0));
3963 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3966 MarkerDrag::~MarkerDrag ()
3968 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3973 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3975 location = new Location (*l);
3976 markers.push_back (m);
3981 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3983 Drag::start_grab (event, cursor);
3987 Location *location = _editor->find_location_from_marker (_marker, is_start);
3988 _editor->_dragging_edit_point = true;
3990 update_item (location);
3992 // _drag_line->show();
3993 // _line->raise_to_top();
3996 show_verbose_cursor_time (location->start());
3998 show_verbose_cursor_time (location->end());
4000 setup_snap_delta (is_start ? location->start() : location->end());
4002 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4005 case Selection::Toggle:
4006 /* we toggle on the button release */
4008 case Selection::Set:
4009 if (!_editor->selection->selected (_marker)) {
4010 _editor->selection->set (_marker);
4011 _selection_changed = true;
4014 case Selection::Extend:
4016 Locations::LocationList ll;
4017 list<ArdourMarker*> to_add;
4019 _editor->selection->markers.range (s, e);
4020 s = min (_marker->position(), s);
4021 e = max (_marker->position(), e);
4024 if (e < max_framepos) {
4027 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4028 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4029 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4032 to_add.push_back (lm->start);
4035 to_add.push_back (lm->end);
4039 if (!to_add.empty()) {
4040 _editor->selection->add (to_add);
4041 _selection_changed = true;
4045 case Selection::Add:
4046 _editor->selection->add (_marker);
4047 _selection_changed = true;
4052 /* Set up copies for us to manipulate during the drag
4055 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4057 Location* l = _editor->find_location_from_marker (*i, is_start);
4064 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4066 /* range: check that the other end of the range isn't
4069 CopiedLocationInfo::iterator x;
4070 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4071 if (*(*x).location == *l) {
4075 if (x == _copied_locations.end()) {
4076 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4078 (*x).markers.push_back (*i);
4079 (*x).move_both = true;
4087 MarkerDrag::setup_pointer_frame_offset ()
4090 Location *location = _editor->find_location_from_marker (_marker, is_start);
4091 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4095 MarkerDrag::motion (GdkEvent* event, bool)
4097 framecnt_t f_delta = 0;
4099 bool move_both = false;
4100 Location *real_location;
4101 Location *copy_location = 0;
4102 framecnt_t const sd = snap_delta (event->button.state);
4104 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4105 framepos_t next = newframe;
4107 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4111 CopiedLocationInfo::iterator x;
4113 /* find the marker we're dragging, and compute the delta */
4115 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4117 copy_location = (*x).location;
4119 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4121 /* this marker is represented by this
4122 * CopiedLocationMarkerInfo
4125 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4130 if (real_location->is_mark()) {
4131 f_delta = newframe - copy_location->start();
4135 switch (_marker->type()) {
4136 case ArdourMarker::SessionStart:
4137 case ArdourMarker::RangeStart:
4138 case ArdourMarker::LoopStart:
4139 case ArdourMarker::PunchIn:
4140 f_delta = newframe - copy_location->start();
4143 case ArdourMarker::SessionEnd:
4144 case ArdourMarker::RangeEnd:
4145 case ArdourMarker::LoopEnd:
4146 case ArdourMarker::PunchOut:
4147 f_delta = newframe - copy_location->end();
4150 /* what kind of marker is this ? */
4159 if (x == _copied_locations.end()) {
4160 /* hmm, impossible - we didn't find the dragged marker */
4164 /* now move them all */
4166 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4168 copy_location = x->location;
4170 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4174 if (real_location->locked()) {
4178 if (copy_location->is_mark()) {
4182 copy_location->set_start (copy_location->start() + f_delta);
4186 framepos_t new_start = copy_location->start() + f_delta;
4187 framepos_t new_end = copy_location->end() + f_delta;
4189 if (is_start) { // start-of-range marker
4191 if (move_both || (*x).move_both) {
4192 copy_location->set_start (new_start);
4193 copy_location->set_end (new_end);
4194 } else if (new_start < copy_location->end()) {
4195 copy_location->set_start (new_start);
4196 } else if (newframe > 0) {
4197 //_editor->snap_to (next, RoundUpAlways, true);
4198 copy_location->set_end (next);
4199 copy_location->set_start (newframe);
4202 } else { // end marker
4204 if (move_both || (*x).move_both) {
4205 copy_location->set_end (new_end);
4206 copy_location->set_start (new_start);
4207 } else if (new_end > copy_location->start()) {
4208 copy_location->set_end (new_end);
4209 } else if (newframe > 0) {
4210 //_editor->snap_to (next, RoundDownAlways, true);
4211 copy_location->set_start (next);
4212 copy_location->set_end (newframe);
4217 update_item (copy_location);
4219 /* now lookup the actual GUI items used to display this
4220 * location and move them to wherever the copy of the location
4221 * is now. This means that the logic in ARDOUR::Location is
4222 * still enforced, even though we are not (yet) modifying
4223 * the real Location itself.
4226 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4229 lm->set_position (copy_location->start(), copy_location->end());
4234 assert (!_copied_locations.empty());
4236 show_verbose_cursor_time (newframe);
4240 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4242 if (!movement_occurred) {
4244 if (was_double_click()) {
4245 _editor->rename_marker (_marker);
4249 /* just a click, do nothing but finish
4250 off the selection process
4253 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4255 case Selection::Set:
4256 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4257 _editor->selection->set (_marker);
4258 _selection_changed = true;
4262 case Selection::Toggle:
4263 /* we toggle on the button release, click only */
4264 _editor->selection->toggle (_marker);
4265 _selection_changed = true;
4269 case Selection::Extend:
4270 case Selection::Add:
4274 if (_selection_changed) {
4275 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4276 _editor->commit_reversible_selection_op();
4282 _editor->_dragging_edit_point = false;
4284 XMLNode &before = _editor->session()->locations()->get_state();
4285 bool in_command = false;
4287 MarkerSelection::iterator i;
4288 CopiedLocationInfo::iterator x;
4291 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4292 x != _copied_locations.end() && i != _editor->selection->markers.end();
4295 Location * location = _editor->find_location_from_marker (*i, is_start);
4299 if (location->locked()) {
4303 _editor->begin_reversible_command ( _("move marker") );
4306 if (location->is_mark()) {
4307 location->set_start (((*x).location)->start());
4309 location->set (((*x).location)->start(), ((*x).location)->end());
4315 XMLNode &after = _editor->session()->locations()->get_state();
4316 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4317 _editor->commit_reversible_command ();
4322 MarkerDrag::aborted (bool movement_occurred)
4324 if (!movement_occurred) {
4328 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4330 /* move all markers to their original location */
4333 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4336 Location * location = _editor->find_location_from_marker (*m, is_start);
4339 (*m)->set_position (is_start ? location->start() : location->end());
4346 MarkerDrag::update_item (Location*)
4351 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4353 , _fixed_grab_x (0.0)
4354 , _fixed_grab_y (0.0)
4355 , _cumulative_x_drag (0.0)
4356 , _cumulative_y_drag (0.0)
4360 if (_zero_gain_fraction < 0.0) {
4361 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4364 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4366 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4372 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4374 Drag::start_grab (event, _editor->cursors()->fader);
4376 // start the grab at the center of the control point so
4377 // the point doesn't 'jump' to the mouse after the first drag
4378 _fixed_grab_x = _point->get_x();
4379 _fixed_grab_y = _point->get_y();
4381 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4382 setup_snap_delta (pos);
4384 float const fraction = 1 - (_point->get_y() / _point->line().height());
4385 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4387 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4389 if (!_point->can_slide ()) {
4390 _x_constrained = true;
4395 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4397 double dx = _drags->current_pointer_x() - last_pointer_x();
4398 double dy = current_pointer_y() - last_pointer_y();
4399 bool need_snap = true;
4401 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4407 /* coordinate in pixels relative to the start of the region (for region-based automation)
4408 or track (for track-based automation) */
4409 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4410 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4412 // calculate zero crossing point. back off by .01 to stay on the
4413 // positive side of zero
4414 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4416 if (_x_constrained) {
4419 if (_y_constrained) {
4423 _cumulative_x_drag = cx - _fixed_grab_x;
4424 _cumulative_y_drag = cy - _fixed_grab_y;
4428 cy = min ((double) _point->line().height(), cy);
4430 // make sure we hit zero when passing through
4431 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4435 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4437 if (!_x_constrained && need_snap) {
4438 _editor->snap_to_with_modifier (cx_frames, event);
4441 cx_frames -= snap_delta (event->button.state);
4442 cx_frames = min (cx_frames, _point->line().maximum_time());
4444 float const fraction = 1.0 - (cy / _point->line().height());
4447 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4448 _editor->begin_reversible_command (_("automation event move"));
4449 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4451 pair<double, float> result;
4452 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4454 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4458 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4460 if (!movement_occurred) {
4463 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4464 _editor->reset_point_selection ();
4468 _point->line().end_drag (_pushing, _final_index);
4469 _editor->commit_reversible_command ();
4474 ControlPointDrag::aborted (bool)
4476 _point->line().reset ();
4480 ControlPointDrag::active (Editing::MouseMode m)
4482 if (m == Editing::MouseDraw) {
4483 /* always active in mouse draw */
4487 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4488 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4491 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4494 , _fixed_grab_x (0.0)
4495 , _fixed_grab_y (0.0)
4496 , _cumulative_y_drag (0)
4500 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4504 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4506 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4509 _item = &_line->grab_item ();
4511 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4512 origin, and ditto for y.
4515 double mx = event->button.x;
4516 double my = event->button.y;
4518 _line->grab_item().canvas_to_item (mx, my);
4520 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4522 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4523 /* no adjacent points */
4527 Drag::start_grab (event, _editor->cursors()->fader);
4529 /* store grab start in item frame */
4530 double const bx = _line->nth (_before)->get_x();
4531 double const ax = _line->nth (_after)->get_x();
4532 double const click_ratio = (ax - mx) / (ax - bx);
4534 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4539 double fraction = 1.0 - (cy / _line->height());
4541 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4545 LineDrag::motion (GdkEvent* event, bool first_move)
4547 double dy = current_pointer_y() - last_pointer_y();
4549 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4553 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4555 _cumulative_y_drag = cy - _fixed_grab_y;
4558 cy = min ((double) _line->height(), cy);
4560 double const fraction = 1.0 - (cy / _line->height());
4564 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4566 _editor->begin_reversible_command (_("automation range move"));
4567 _line->start_drag_line (_before, _after, initial_fraction);
4570 /* we are ignoring x position for this drag, so we can just pass in anything */
4571 pair<double, float> result;
4573 result = _line->drag_motion (0, fraction, true, false, ignored);
4574 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4578 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4580 if (movement_occurred) {
4581 motion (event, false);
4582 _line->end_drag (false, 0);
4583 _editor->commit_reversible_command ();
4585 /* add a new control point on the line */
4587 AutomationTimeAxisView* atv;
4589 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4590 framepos_t where = grab_frame ();
4593 double cy = _fixed_grab_y;
4595 _line->grab_item().item_to_canvas (cx, cy);
4597 atv->add_automation_event (event, where, cy, false);
4598 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4599 AudioRegionView* arv;
4601 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4602 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4609 LineDrag::aborted (bool)
4614 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4618 _region_view_grab_x (0.0),
4619 _cumulative_x_drag (0),
4623 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4627 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4629 Drag::start_grab (event);
4631 _line = reinterpret_cast<Line*> (_item);
4634 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4636 double cx = event->button.x;
4637 double cy = event->button.y;
4639 _item->parent()->canvas_to_item (cx, cy);
4641 /* store grab start in parent frame */
4642 _region_view_grab_x = cx;
4644 _before = *(float*) _item->get_data ("position");
4646 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4648 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4652 FeatureLineDrag::motion (GdkEvent*, bool)
4654 double dx = _drags->current_pointer_x() - last_pointer_x();
4656 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4658 _cumulative_x_drag += dx;
4660 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4669 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4671 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4673 float *pos = new float;
4676 _line->set_data ("position", pos);
4682 FeatureLineDrag::finished (GdkEvent*, bool)
4684 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4685 _arv->update_transient(_before, _before);
4689 FeatureLineDrag::aborted (bool)
4694 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4696 , _vertical_only (false)
4698 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4702 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4704 Drag::start_grab (event);
4705 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4709 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4716 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4718 framepos_t grab = grab_frame ();
4719 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4720 _editor->snap_to_with_modifier (grab, event);
4722 grab = raw_grab_frame ();
4725 /* base start and end on initial click position */
4735 if (current_pointer_y() < grab_y()) {
4736 y1 = current_pointer_y();
4739 y2 = current_pointer_y();
4743 if (start != end || y1 != y2) {
4745 double x1 = _editor->sample_to_pixel (start);
4746 double x2 = _editor->sample_to_pixel (end);
4747 const double min_dimension = 2.0;
4749 if (_vertical_only) {
4750 /* fixed 10 pixel width */
4754 x2 = min (x1 - min_dimension, x2);
4756 x2 = max (x1 + min_dimension, x2);
4761 y2 = min (y1 - min_dimension, y2);
4763 y2 = max (y1 + min_dimension, y2);
4766 /* translate rect into item space and set */
4768 ArdourCanvas::Rect r (x1, y1, x2, y2);
4770 /* this drag is a _trackview_only == true drag, so the y1 and
4771 * y2 (computed using current_pointer_y() and grab_y()) will be
4772 * relative to the top of the trackview group). The
4773 * rubberband rect has the same parent/scroll offset as the
4774 * the trackview group, so we can use the "r" rect directly
4775 * to set the shape of the rubberband.
4778 _editor->rubberband_rect->set (r);
4779 _editor->rubberband_rect->show();
4780 _editor->rubberband_rect->raise_to_top();
4782 show_verbose_cursor_time (pf);
4784 do_select_things (event, true);
4789 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4793 framepos_t grab = grab_frame ();
4794 framepos_t lpf = last_pointer_frame ();
4796 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4797 grab = raw_grab_frame ();
4798 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4812 if (current_pointer_y() < grab_y()) {
4813 y1 = current_pointer_y();
4816 y2 = current_pointer_y();
4820 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4824 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4826 if (movement_occurred) {
4828 motion (event, false);
4829 do_select_things (event, false);
4835 bool do_deselect = true;
4836 MidiTimeAxisView* mtv;
4838 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4840 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4841 /* nothing selected */
4842 add_midi_region (mtv, true);
4843 do_deselect = false;
4847 /* do not deselect if Primary or Tertiary (toggle-select or
4848 * extend-select are pressed.
4851 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4852 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4859 _editor->rubberband_rect->hide();
4863 RubberbandSelectDrag::aborted (bool)
4865 _editor->rubberband_rect->hide ();
4868 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4869 : RegionDrag (e, i, p, v)
4871 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4875 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4877 Drag::start_grab (event, cursor);
4879 _editor->get_selection().add (_primary);
4881 framepos_t where = _primary->region()->position();
4882 setup_snap_delta (where);
4884 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4888 TimeFXDrag::motion (GdkEvent* event, bool)
4890 RegionView* rv = _primary;
4891 StreamView* cv = rv->get_time_axis_view().view ();
4893 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4894 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4895 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4896 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4897 _editor->snap_to_with_modifier (pf, event);
4898 pf -= snap_delta (event->button.state);
4900 if (pf > rv->region()->position()) {
4901 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4904 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4908 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4910 /* this may have been a single click, no drag. We still want the dialog
4911 to show up in that case, so that the user can manually edit the
4912 parameters for the timestretch.
4915 float fraction = 1.0;
4917 if (movement_occurred) {
4919 motion (event, false);
4921 _primary->get_time_axis_view().hide_timestretch ();
4923 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4925 if (adjusted_frame_pos < _primary->region()->position()) {
4926 /* backwards drag of the left edge - not usable */
4930 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4932 fraction = (double) newlen / (double) _primary->region()->length();
4934 #ifndef USE_RUBBERBAND
4935 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4936 if (_primary->region()->data_type() == DataType::AUDIO) {
4937 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4942 if (!_editor->get_selection().regions.empty()) {
4943 /* primary will already be included in the selection, and edit
4944 group shared editing will propagate selection across
4945 equivalent regions, so just use the current region
4949 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4950 error << _("An error occurred while executing time stretch operation") << endmsg;
4956 TimeFXDrag::aborted (bool)
4958 _primary->get_time_axis_view().hide_timestretch ();
4961 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4964 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4968 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4970 Drag::start_grab (event);
4974 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4976 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4980 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4982 if (movement_occurred && _editor->session()) {
4983 /* make sure we stop */
4984 _editor->session()->request_transport_speed (0.0);
4989 ScrubDrag::aborted (bool)
4994 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4998 , _time_selection_at_start (!_editor->get_selection().time.empty())
5000 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5002 if (_time_selection_at_start) {
5003 start_at_start = _editor->get_selection().time.start();
5004 end_at_start = _editor->get_selection().time.end_frame();
5009 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5011 if (_editor->session() == 0) {
5015 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5017 switch (_operation) {
5018 case CreateSelection:
5019 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5024 cursor = _editor->cursors()->selector;
5025 Drag::start_grab (event, cursor);
5028 case SelectionStartTrim:
5029 if (_editor->clicked_axisview) {
5030 _editor->clicked_axisview->order_selection_trims (_item, true);
5032 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5035 case SelectionEndTrim:
5036 if (_editor->clicked_axisview) {
5037 _editor->clicked_axisview->order_selection_trims (_item, false);
5039 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5043 Drag::start_grab (event, cursor);
5046 case SelectionExtend:
5047 Drag::start_grab (event, cursor);
5051 if (_operation == SelectionMove) {
5052 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5054 show_verbose_cursor_time (adjusted_current_frame (event));
5059 SelectionDrag::setup_pointer_frame_offset ()
5061 switch (_operation) {
5062 case CreateSelection:
5063 _pointer_frame_offset = 0;
5066 case SelectionStartTrim:
5068 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5071 case SelectionEndTrim:
5072 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5075 case SelectionExtend:
5081 SelectionDrag::motion (GdkEvent* event, bool first_move)
5083 framepos_t start = 0;
5085 framecnt_t length = 0;
5086 framecnt_t distance = 0;
5088 framepos_t const pending_position = adjusted_current_frame (event);
5090 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5094 switch (_operation) {
5095 case CreateSelection:
5097 framepos_t grab = grab_frame ();
5100 grab = adjusted_current_frame (event, false);
5101 if (grab < pending_position) {
5102 _editor->snap_to (grab, RoundDownMaybe);
5104 _editor->snap_to (grab, RoundUpMaybe);
5108 if (pending_position < grab) {
5109 start = pending_position;
5112 end = pending_position;
5116 /* first drag: Either add to the selection
5117 or create a new selection
5124 /* adding to the selection */
5125 _editor->set_selected_track_as_side_effect (Selection::Add);
5126 _editor->clicked_selection = _editor->selection->add (start, end);
5133 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5134 _editor->set_selected_track_as_side_effect (Selection::Set);
5137 _editor->clicked_selection = _editor->selection->set (start, end);
5141 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5142 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5143 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5145 _editor->selection->add (atest);
5149 /* select all tracks within the rectangle that we've marked out so far */
5150 TrackViewList new_selection;
5151 TrackViewList& all_tracks (_editor->track_views);
5153 ArdourCanvas::Coord const top = grab_y();
5154 ArdourCanvas::Coord const bottom = current_pointer_y();
5156 if (top >= 0 && bottom >= 0) {
5158 //first, find the tracks that are covered in the y range selection
5159 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5160 if ((*i)->covered_by_y_range (top, bottom)) {
5161 new_selection.push_back (*i);
5165 //now find any tracks that are GROUPED with the tracks we selected
5166 TrackViewList grouped_add = new_selection;
5167 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5168 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5169 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
5170 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5171 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5172 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5173 grouped_add.push_back (*j);
5178 //now compare our list with the current selection, and add or remove as necessary
5179 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5180 TrackViewList tracks_to_add;
5181 TrackViewList tracks_to_remove;
5182 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5183 if ( !_editor->selection->tracks.contains ( *i ) )
5184 tracks_to_add.push_back ( *i );
5185 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5186 if ( !grouped_add.contains ( *i ) )
5187 tracks_to_remove.push_back ( *i );
5188 _editor->selection->add(tracks_to_add);
5189 _editor->selection->remove(tracks_to_remove);
5195 case SelectionStartTrim:
5197 end = _editor->selection->time[_editor->clicked_selection].end;
5199 if (pending_position > end) {
5202 start = pending_position;
5206 case SelectionEndTrim:
5208 start = _editor->selection->time[_editor->clicked_selection].start;
5210 if (pending_position < start) {
5213 end = pending_position;
5220 start = _editor->selection->time[_editor->clicked_selection].start;
5221 end = _editor->selection->time[_editor->clicked_selection].end;
5223 length = end - start;
5224 distance = pending_position - start;
5225 start = pending_position;
5226 _editor->snap_to (start);
5228 end = start + length;
5232 case SelectionExtend:
5237 switch (_operation) {
5239 if (_time_selection_at_start) {
5240 _editor->selection->move_time (distance);
5244 _editor->selection->replace (_editor->clicked_selection, start, end);
5248 if (_operation == SelectionMove) {
5249 show_verbose_cursor_time(start);
5251 show_verbose_cursor_time(pending_position);
5256 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5258 Session* s = _editor->session();
5260 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5261 if (movement_occurred) {
5262 motion (event, false);
5263 /* XXX this is not object-oriented programming at all. ick */
5264 if (_editor->selection->time.consolidate()) {
5265 _editor->selection->TimeChanged ();
5268 /* XXX what if its a music time selection? */
5270 if (s->get_play_range() && s->transport_rolling()) {
5271 s->request_play_range (&_editor->selection->time, true);
5272 } else if (!s->config.get_external_sync()) {
5273 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5274 if (_operation == SelectionEndTrim)
5275 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5277 s->request_locate (_editor->get_selection().time.start());
5281 if (_editor->get_selection().time.length() != 0) {
5282 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5284 s->clear_range_selection ();
5289 /* just a click, no pointer movement.
5292 if (_operation == SelectionExtend) {
5293 if (_time_selection_at_start) {
5294 framepos_t pos = adjusted_current_frame (event, false);
5295 framepos_t start = min (pos, start_at_start);
5296 framepos_t end = max (pos, end_at_start);
5297 _editor->selection->set (start, end);
5300 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5301 if (_editor->clicked_selection) {
5302 _editor->selection->remove (_editor->clicked_selection);
5305 if (!_editor->clicked_selection) {
5306 _editor->selection->clear_time();
5311 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5312 _editor->selection->set (_editor->clicked_axisview);
5315 if (s && s->get_play_range () && s->transport_rolling()) {
5316 s->request_stop (false, false);
5321 _editor->stop_canvas_autoscroll ();
5322 _editor->clicked_selection = 0;
5323 _editor->commit_reversible_selection_op ();
5327 SelectionDrag::aborted (bool)
5332 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5333 : Drag (e, i, false),
5337 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5339 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5340 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5341 physical_screen_height (_editor->current_toplevel()->get_window())));
5342 _drag_rect->hide ();
5344 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5345 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5348 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5350 /* normal canvas items will be cleaned up when their parent group is deleted. But
5351 this item is created as the child of a long-lived parent group, and so we
5352 need to explicitly delete it.
5358 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5360 if (_editor->session() == 0) {
5364 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5366 if (!_editor->temp_location) {
5367 _editor->temp_location = new Location (*_editor->session());
5370 switch (_operation) {
5371 case CreateSkipMarker:
5372 case CreateRangeMarker:
5373 case CreateTransportMarker:
5374 case CreateCDMarker:
5376 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5381 cursor = _editor->cursors()->selector;
5385 Drag::start_grab (event, cursor);
5387 show_verbose_cursor_time (adjusted_current_frame (event));
5391 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5393 framepos_t start = 0;
5395 ArdourCanvas::Rectangle *crect;
5397 switch (_operation) {
5398 case CreateSkipMarker:
5399 crect = _editor->range_bar_drag_rect;
5401 case CreateRangeMarker:
5402 crect = _editor->range_bar_drag_rect;
5404 case CreateTransportMarker:
5405 crect = _editor->transport_bar_drag_rect;
5407 case CreateCDMarker:
5408 crect = _editor->cd_marker_bar_drag_rect;
5411 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5416 framepos_t const pf = adjusted_current_frame (event);
5418 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5419 framepos_t grab = grab_frame ();
5420 _editor->snap_to (grab);
5422 if (pf < grab_frame()) {
5430 /* first drag: Either add to the selection
5431 or create a new selection.
5436 _editor->temp_location->set (start, end);
5440 update_item (_editor->temp_location);
5442 //_drag_rect->raise_to_top();
5448 _editor->temp_location->set (start, end);
5450 double x1 = _editor->sample_to_pixel (start);
5451 double x2 = _editor->sample_to_pixel (end);
5455 update_item (_editor->temp_location);
5458 show_verbose_cursor_time (pf);
5463 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5465 Location * newloc = 0;
5469 if (movement_occurred) {
5470 motion (event, false);
5473 switch (_operation) {
5474 case CreateSkipMarker:
5475 case CreateRangeMarker:
5476 case CreateCDMarker:
5478 XMLNode &before = _editor->session()->locations()->get_state();
5479 if (_operation == CreateSkipMarker) {
5480 _editor->begin_reversible_command (_("new skip marker"));
5481 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5482 flags = Location::IsRangeMarker | Location::IsSkip;
5483 _editor->range_bar_drag_rect->hide();
5484 } else if (_operation == CreateCDMarker) {
5485 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5486 _editor->begin_reversible_command (_("new CD marker"));
5487 flags = Location::IsRangeMarker | Location::IsCDMarker;
5488 _editor->cd_marker_bar_drag_rect->hide();
5490 _editor->begin_reversible_command (_("new skip marker"));
5491 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5492 flags = Location::IsRangeMarker;
5493 _editor->range_bar_drag_rect->hide();
5495 newloc = new Location (
5496 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5499 _editor->session()->locations()->add (newloc, true);
5500 XMLNode &after = _editor->session()->locations()->get_state();
5501 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5502 _editor->commit_reversible_command ();
5506 case CreateTransportMarker:
5507 // popup menu to pick loop or punch
5508 _editor->new_transport_marker_context_menu (&event->button, _item);
5514 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5516 if (_operation == CreateTransportMarker) {
5518 /* didn't drag, so just locate */
5520 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5522 } else if (_operation == CreateCDMarker) {
5524 /* didn't drag, but mark is already created so do
5527 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5532 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5534 if (end == max_framepos) {
5535 end = _editor->session()->current_end_frame ();
5538 if (start == max_framepos) {
5539 start = _editor->session()->current_start_frame ();
5542 switch (_editor->mouse_mode) {
5544 /* find the two markers on either side and then make the selection from it */
5545 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5549 /* find the two markers on either side of the click and make the range out of it */
5550 _editor->selection->set (start, end);
5559 _editor->stop_canvas_autoscroll ();
5563 RangeMarkerBarDrag::aborted (bool movement_occurred)
5565 if (movement_occurred) {
5566 _drag_rect->hide ();
5571 RangeMarkerBarDrag::update_item (Location* location)
5573 double const x1 = _editor->sample_to_pixel (location->start());
5574 double const x2 = _editor->sample_to_pixel (location->end());
5576 _drag_rect->set_x0 (x1);
5577 _drag_rect->set_x1 (x2);
5580 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5582 , _cumulative_dx (0)
5583 , _cumulative_dy (0)
5584 , _was_selected (false)
5586 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5588 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5590 _region = &_primary->region_view ();
5591 _note_height = _region->midi_stream_view()->note_height ();
5595 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5597 Drag::start_grab (event);
5598 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5600 if (!(_was_selected = _primary->selected())) {
5602 /* tertiary-click means extend selection - we'll do that on button release,
5603 so don't add it here, because otherwise we make it hard to figure
5604 out the "extend-to" range.
5607 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5610 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5613 _region->note_selected (_primary, true);
5615 _editor->get_selection().clear_points();
5616 _region->unique_select (_primary);
5622 /** @return Current total drag x change in frames */
5624 NoteDrag::total_dx (const guint state) const
5627 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5629 /* primary note time */
5630 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5632 /* new time of the primary note in session frames */
5633 frameoffset_t st = n + dx + snap_delta (state);
5635 framepos_t const rp = _region->region()->position ();
5637 /* prevent the note being dragged earlier than the region's position */
5640 /* possibly snap and return corresponding delta */
5644 if (ArdourKeyboard::indicates_snap (state)) {
5645 if (_editor->snap_mode () != SnapOff) {
5649 if (_editor->snap_mode () == SnapOff) {
5651 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5652 if (ArdourKeyboard::indicates_snap_delta (state)) {
5660 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5661 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5663 ret = st - n - snap_delta (state);
5668 /** @return Current total drag y change in note number */
5670 NoteDrag::total_dy () const
5672 MidiStreamView* msv = _region->midi_stream_view ();
5673 double const y = _region->midi_view()->y_position ();
5674 /* new current note */
5675 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5677 n = max (msv->lowest_note(), n);
5678 n = min (msv->highest_note(), n);
5679 /* and work out delta */
5680 return n - msv->y_to_note (grab_y() - y);
5684 NoteDrag::motion (GdkEvent * event, bool)
5686 /* Total change in x and y since the start of the drag */
5687 frameoffset_t const dx = total_dx (event->button.state);
5688 int8_t const dy = total_dy ();
5690 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5691 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5692 double const tdy = -dy * _note_height - _cumulative_dy;
5695 _cumulative_dx += tdx;
5696 _cumulative_dy += tdy;
5698 int8_t note_delta = total_dy();
5700 _region->move_selection (tdx, tdy, note_delta);
5702 /* the new note value may be the same as the old one, but we
5703 * don't know what that means because the selection may have
5704 * involved more than one note and we might be doing something
5705 * odd with them. so show the note value anyway, always.
5708 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5710 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5715 NoteDrag::finished (GdkEvent* ev, bool moved)
5718 /* no motion - select note */
5720 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5721 _editor->current_mouse_mode() == Editing::MouseDraw) {
5723 bool changed = false;
5725 if (_was_selected) {
5726 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5728 _region->note_deselected (_primary);
5731 _editor->get_selection().clear_points();
5732 _region->unique_select (_primary);
5736 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5737 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5739 if (!extend && !add && _region->selection_size() > 1) {
5740 _editor->get_selection().clear_points();
5741 _region->unique_select (_primary);
5743 } else if (extend) {
5744 _region->note_selected (_primary, true, true);
5747 /* it was added during button press */
5754 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5755 _editor->commit_reversible_selection_op();
5759 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5764 NoteDrag::aborted (bool)
5769 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5770 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5771 : Drag (editor, atv->base_item ())
5773 , _y_origin (atv->y_position())
5774 , _nothing_to_drag (false)
5776 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5777 setup (atv->lines ());
5780 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5781 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5782 : Drag (editor, rv->get_canvas_group ())
5784 , _y_origin (rv->get_time_axis_view().y_position())
5785 , _nothing_to_drag (false)
5788 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5790 list<boost::shared_ptr<AutomationLine> > lines;
5792 AudioRegionView* audio_view;
5793 AutomationRegionView* automation_view;
5794 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5795 lines.push_back (audio_view->get_gain_line ());
5796 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5797 lines.push_back (automation_view->line ());
5800 error << _("Automation range drag created for invalid region type") << endmsg;
5806 /** @param lines AutomationLines to drag.
5807 * @param offset Offset from the session start to the points in the AutomationLines.
5810 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5812 /* find the lines that overlap the ranges being dragged */
5813 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5814 while (i != lines.end ()) {
5815 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5818 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5820 /* check this range against all the AudioRanges that we are using */
5821 list<AudioRange>::const_iterator k = _ranges.begin ();
5822 while (k != _ranges.end()) {
5823 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5829 /* add it to our list if it overlaps at all */
5830 if (k != _ranges.end()) {
5835 _lines.push_back (n);
5841 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5845 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5847 return 1.0 - ((global_y - _y_origin) / line->height());
5851 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5853 const double v = list->eval(x);
5854 return _integral ? rint(v) : v;
5858 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5860 Drag::start_grab (event, cursor);
5862 /* Get line states before we start changing things */
5863 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5864 i->state = &i->line->get_state ();
5865 i->original_fraction = y_fraction (i->line, current_pointer_y());
5868 if (_ranges.empty()) {
5870 /* No selected time ranges: drag all points */
5871 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5872 uint32_t const N = i->line->npoints ();
5873 for (uint32_t j = 0; j < N; ++j) {
5874 i->points.push_back (i->line->nth (j));
5880 if (_nothing_to_drag) {
5886 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5888 if (_nothing_to_drag && !first_move) {
5893 _editor->begin_reversible_command (_("automation range move"));
5895 if (!_ranges.empty()) {
5897 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5899 framecnt_t const half = (i->start + i->end) / 2;
5901 /* find the line that this audio range starts in */
5902 list<Line>::iterator j = _lines.begin();
5903 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5907 if (j != _lines.end()) {
5908 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5910 /* j is the line that this audio range starts in; fade into it;
5911 64 samples length plucked out of thin air.
5914 framepos_t a = i->start + 64;
5919 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5920 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5922 XMLNode &before = the_list->get_state();
5923 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5924 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5926 if (add_p || add_q) {
5927 _editor->session()->add_command (
5928 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5932 /* same thing for the end */
5935 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5939 if (j != _lines.end()) {
5940 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5942 /* j is the line that this audio range starts in; fade out of it;
5943 64 samples length plucked out of thin air.
5946 framepos_t b = i->end - 64;
5951 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5952 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5954 XMLNode &before = the_list->get_state();
5955 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5956 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5958 if (add_p || add_q) {
5959 _editor->session()->add_command (
5960 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5965 _nothing_to_drag = true;
5967 /* Find all the points that should be dragged and put them in the relevant
5968 points lists in the Line structs.
5971 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5973 uint32_t const N = i->line->npoints ();
5974 for (uint32_t j = 0; j < N; ++j) {
5976 /* here's a control point on this line */
5977 ControlPoint* p = i->line->nth (j);
5978 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5980 /* see if it's inside a range */
5981 list<AudioRange>::const_iterator k = _ranges.begin ();
5982 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5986 if (k != _ranges.end()) {
5987 /* dragging this point */
5988 _nothing_to_drag = false;
5989 i->points.push_back (p);
5995 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5996 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6000 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6001 float const f = y_fraction (l->line, current_pointer_y());
6002 /* we are ignoring x position for this drag, so we can just pass in anything */
6003 pair<double, float> result;
6005 result = l->line->drag_motion (0, f, true, false, ignored);
6006 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6011 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6013 if (_nothing_to_drag || !motion_occurred) {
6017 motion (event, false);
6018 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6019 i->line->end_drag (false, 0);
6022 _editor->commit_reversible_command ();
6026 AutomationRangeDrag::aborted (bool)
6028 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6033 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6035 , initial_time_axis_view (itav)
6037 /* note that time_axis_view may be null if the regionview was created
6038 * as part of a copy operation.
6040 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6041 layer = v->region()->layer ();
6042 initial_y = v->get_canvas_group()->position().y;
6043 initial_playlist = v->region()->playlist ();
6044 initial_position = v->region()->position ();
6045 initial_end = v->region()->position () + v->region()->length ();
6048 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6049 : Drag (e, i->canvas_item ())
6052 , _cumulative_dx (0)
6054 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6055 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6060 PatchChangeDrag::motion (GdkEvent* ev, bool)
6062 framepos_t f = adjusted_current_frame (ev);
6063 boost::shared_ptr<Region> r = _region_view->region ();
6064 f = max (f, r->position ());
6065 f = min (f, r->last_frame ());
6067 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6068 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6069 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6070 _cumulative_dx = dxu;
6074 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6076 if (!movement_occurred) {
6080 boost::shared_ptr<Region> r (_region_view->region ());
6081 framepos_t f = adjusted_current_frame (ev);
6082 f = max (f, r->position ());
6083 f = min (f, r->last_frame ());
6085 _region_view->move_patch_change (
6087 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6092 PatchChangeDrag::aborted (bool)
6094 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6098 PatchChangeDrag::setup_pointer_frame_offset ()
6100 boost::shared_ptr<Region> region = _region_view->region ();
6101 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6104 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6105 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6112 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6114 _region_view->update_drag_selection (
6116 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6120 MidiRubberbandSelectDrag::deselect_things ()
6125 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6126 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6129 _vertical_only = true;
6133 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6135 double const y = _region_view->midi_view()->y_position ();
6137 y1 = max (0.0, y1 - y);
6138 y2 = max (0.0, y2 - y);
6140 _region_view->update_vertical_drag_selection (
6143 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6148 MidiVerticalSelectDrag::deselect_things ()
6153 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6154 : RubberbandSelectDrag (e, i)
6160 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6162 if (drag_in_progress) {
6163 /* We just want to select things at the end of the drag, not during it */
6167 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6169 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6171 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6173 _editor->commit_reversible_selection_op ();
6177 EditorRubberbandSelectDrag::deselect_things ()
6179 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6181 _editor->selection->clear_tracks();
6182 _editor->selection->clear_regions();
6183 _editor->selection->clear_points ();
6184 _editor->selection->clear_lines ();
6185 _editor->selection->clear_midi_notes ();
6187 _editor->commit_reversible_selection_op();
6190 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6195 _note[0] = _note[1] = 0;
6198 NoteCreateDrag::~NoteCreateDrag ()
6204 NoteCreateDrag::grid_frames (framepos_t t) const
6207 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6209 grid_beats = Evoral::Beats(1);
6212 return _region_view->region_beats_to_region_frames (grid_beats);
6216 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6218 Drag::start_grab (event, cursor);
6220 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6222 framepos_t pf = _drags->current_pointer_frame ();
6223 framecnt_t const g = grid_frames (pf);
6225 /* Hack so that we always snap to the note that we are over, instead of snapping
6226 to the next one if we're more than halfway through the one we're over.
6228 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6232 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6233 _note[1] = _note[0];
6235 MidiStreamView* sv = _region_view->midi_stream_view ();
6236 double const x = _editor->sample_to_pixel (_note[0]);
6237 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6239 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6240 _drag_rect->set_outline_all ();
6241 _drag_rect->set_outline_color (0xffffff99);
6242 _drag_rect->set_fill_color (0xffffff66);
6246 NoteCreateDrag::motion (GdkEvent* event, bool)
6248 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6249 double const x0 = _editor->sample_to_pixel (_note[0]);
6250 double const x1 = _editor->sample_to_pixel (_note[1]);
6251 _drag_rect->set_x0 (std::min(x0, x1));
6252 _drag_rect->set_x1 (std::max(x0, x1));
6256 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6258 if (!had_movement) {
6262 framepos_t const start = min (_note[0], _note[1]);
6263 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6265 framecnt_t const g = grid_frames (start);
6266 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6268 if (_editor->snap_mode() == SnapNormal && length < g) {
6272 Evoral::Beats length_beats = max (
6273 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6275 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6279 NoteCreateDrag::y_to_region (double y) const
6282 _region_view->get_canvas_group()->canvas_to_item (x, y);
6287 NoteCreateDrag::aborted (bool)
6292 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6297 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6301 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6303 Drag::start_grab (event, cursor);
6307 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6313 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6316 distance = _drags->current_pointer_x() - grab_x();
6317 len = ar->fade_in()->back()->when;
6319 distance = grab_x() - _drags->current_pointer_x();
6320 len = ar->fade_out()->back()->when;
6323 /* how long should it be ? */
6325 new_length = len + _editor->pixel_to_sample (distance);
6327 /* now check with the region that this is legal */
6329 new_length = ar->verify_xfade_bounds (new_length, start);
6332 arv->reset_fade_in_shape_width (ar, new_length);
6334 arv->reset_fade_out_shape_width (ar, new_length);
6339 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6345 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6348 distance = _drags->current_pointer_x() - grab_x();
6349 len = ar->fade_in()->back()->when;
6351 distance = grab_x() - _drags->current_pointer_x();
6352 len = ar->fade_out()->back()->when;
6355 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6357 _editor->begin_reversible_command ("xfade trim");
6358 ar->playlist()->clear_owned_changes ();
6361 ar->set_fade_in_length (new_length);
6363 ar->set_fade_out_length (new_length);
6366 /* Adjusting the xfade may affect other regions in the playlist, so we need
6367 to get undo Commands from the whole playlist rather than just the
6371 vector<Command*> cmds;
6372 ar->playlist()->rdiff (cmds);
6373 _editor->session()->add_commands (cmds);
6374 _editor->commit_reversible_command ();
6379 CrossfadeEdgeDrag::aborted (bool)
6382 // arv->redraw_start_xfade ();
6384 // arv->redraw_end_xfade ();
6388 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6389 : Drag (e, item, true)
6390 , line (new EditorCursor (*e))
6392 line->set_position (pos);
6396 RegionCutDrag::~RegionCutDrag ()
6402 RegionCutDrag::motion (GdkEvent*, bool)
6404 framepos_t where = _drags->current_pointer_frame();
6405 _editor->snap_to (where);
6407 line->set_position (where);
6411 RegionCutDrag::finished (GdkEvent*, bool)
6413 _editor->get_track_canvas()->canvas()->re_enter();
6415 framepos_t pos = _drags->current_pointer_frame();
6419 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6425 _editor->split_regions_at (pos, rs);
6429 RegionCutDrag::aborted (bool)