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);
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)
515 if (_editor->session()) {
516 const TempoMap& map (_editor->session()->tempo_map());
517 framecnt_t pos = grab_frame();
518 const Meter& m = map.meter_at (pos);
519 /* not that the frame rate used here can be affected by pull up/down which
522 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
523 return view->add_region (grab_frame(), len, true);
526 return boost::shared_ptr<Region>();
529 struct EditorOrderTimeAxisViewSorter {
530 bool operator() (TimeAxisView* a, TimeAxisView* b) {
531 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
532 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
534 return ra->route()->order_key () < rb->route()->order_key ();
538 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
543 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
545 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
546 as some of the regions we are dragging may be on such tracks.
549 TrackViewList track_views = _editor->track_views;
550 track_views.sort (EditorOrderTimeAxisViewSorter ());
552 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
553 _time_axis_views.push_back (*i);
555 TimeAxisView::Children children_list = (*i)->get_child_list ();
556 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
557 _time_axis_views.push_back (j->get());
561 /* the list of views can be empty at this point if this is a region list-insert drag
564 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
565 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
568 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
572 RegionDrag::region_going_away (RegionView* v)
574 list<DraggingView>::iterator i = _views.begin ();
575 while (i != _views.end() && i->view != v) {
579 if (i != _views.end()) {
584 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
585 * or -1 if it is not found.
588 RegionDrag::find_time_axis_view (TimeAxisView* t) const
591 int const N = _time_axis_views.size ();
592 while (i < N && _time_axis_views[i] != t) {
596 if (_time_axis_views[i] != t) {
603 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
604 : RegionDrag (e, i, p, v)
606 , _ignore_video_lock (false)
608 , _last_pointer_time_axis_view (0)
609 , _last_pointer_layer (0)
614 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
618 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
620 Drag::start_grab (event, cursor);
621 setup_snap_delta (_last_frame_position);
623 show_verbose_cursor_time (_last_frame_position);
625 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
627 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
628 assert(_last_pointer_time_axis_view >= 0);
629 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
632 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
633 _ignore_video_lock = true;
637 /* cross track dragging seems broken here. disabled for now. */
638 _y_constrained = true;
643 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
645 /* compute the amount of pointer motion in frames, and where
646 the region would be if we moved it by that much.
648 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
650 framepos_t sync_frame;
651 framecnt_t sync_offset;
654 sync_offset = _primary->region()->sync_offset (sync_dir);
656 /* we don't handle a sync point that lies before zero.
658 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
660 sync_frame = *pending_region_position + (sync_dir * sync_offset);
662 _editor->snap_to_with_modifier (sync_frame, event);
664 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
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 _region = add_midi_region (_view);
2307 _view->playlist()->freeze ();
2310 framepos_t const f = adjusted_current_frame (event);
2311 if (f < grab_frame()) {
2312 _region->set_position (f);
2315 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2316 so that if this region is duplicated, its duplicate starts on
2317 a snap point rather than 1 frame after a snap point. Otherwise things get
2318 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2319 place snapped notes at the start of the region.
2322 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2323 _region->set_length (len < 1 ? 1 : len);
2329 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2331 if (!movement_occurred) {
2332 add_midi_region (_view);
2334 _view->playlist()->thaw ();
2339 RegionCreateDrag::aborted (bool)
2342 _view->playlist()->thaw ();
2348 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2353 , _was_selected (false)
2356 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2360 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2362 Gdk::Cursor* cursor;
2363 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2365 float x_fraction = cnote->mouse_x_fraction ();
2367 if (x_fraction > 0.0 && x_fraction < 0.25) {
2368 cursor = _editor->cursors()->left_side_trim;
2371 cursor = _editor->cursors()->right_side_trim;
2375 Drag::start_grab (event, cursor);
2377 region = &cnote->region_view();
2380 temp = region->snap_to_pixel (cnote->x0 (), true);
2381 _snap_delta = temp - cnote->x0 ();
2385 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2390 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2391 if (ms.size() > 1) {
2392 /* has to be relative, may make no sense otherwise */
2396 if (!(_was_selected = cnote->selected())) {
2398 /* tertiary-click means extend selection - we'll do that on button release,
2399 so don't add it here, because otherwise we make it hard to figure
2400 out the "extend-to" range.
2403 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2406 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2409 region->note_selected (cnote, true);
2411 _editor->get_selection().clear_points();
2412 region->unique_select (cnote);
2419 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2421 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2423 _editor->begin_reversible_command (_("resize notes"));
2425 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2426 MidiRegionSelection::iterator next;
2429 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2431 mrv->begin_resizing (at_front);
2437 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2438 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2440 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2444 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2446 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2447 if (_editor->snap_mode () != SnapOff) {
2451 if (_editor->snap_mode () == SnapOff) {
2453 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2454 if (apply_snap_delta) {
2460 if (apply_snap_delta) {
2464 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2470 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2472 if (!movement_occurred) {
2473 /* no motion - select note */
2474 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2475 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2476 _editor->current_mouse_mode() == Editing::MouseDraw) {
2478 bool changed = false;
2480 if (_was_selected) {
2481 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2483 region->note_deselected (cnote);
2486 _editor->get_selection().clear_points();
2487 region->unique_select (cnote);
2491 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2492 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2494 if (!extend && !add && region->selection_size() > 1) {
2495 _editor->get_selection().clear_points();
2496 region->unique_select (cnote);
2498 } else if (extend) {
2499 region->note_selected (cnote, true, true);
2502 /* it was added during button press */
2508 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2509 _editor->commit_reversible_selection_op();
2516 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2517 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2518 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2520 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2523 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2525 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2526 if (_editor->snap_mode () != SnapOff) {
2530 if (_editor->snap_mode () == SnapOff) {
2532 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2533 if (apply_snap_delta) {
2539 if (apply_snap_delta) {
2543 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2547 _editor->commit_reversible_command ();
2551 NoteResizeDrag::aborted (bool)
2553 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2554 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2555 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2557 mrv->abort_resizing ();
2562 AVDraggingView::AVDraggingView (RegionView* v)
2565 initial_position = v->region()->position ();
2568 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2571 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2574 TrackViewList empty;
2576 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2577 std::list<RegionView*> views = rs.by_layer();
2580 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2581 RegionView* rv = (*i);
2582 if (!rv->region()->video_locked()) {
2585 if (rv->region()->locked()) {
2588 _views.push_back (AVDraggingView (rv));
2593 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2595 Drag::start_grab (event);
2596 if (_editor->session() == 0) {
2600 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2606 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2610 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2611 _max_backwards_drag = (
2612 ARDOUR_UI::instance()->video_timeline->get_duration()
2613 + ARDOUR_UI::instance()->video_timeline->get_offset()
2614 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2617 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2618 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2619 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2622 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2625 Timecode::Time timecode;
2626 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2627 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);
2628 show_verbose_cursor_text (buf);
2632 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2634 if (_editor->session() == 0) {
2637 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2641 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2645 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2646 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2648 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2649 dt = - _max_backwards_drag;
2652 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2653 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2655 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2656 RegionView* rv = i->view;
2657 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2660 rv->region()->clear_changes ();
2661 rv->region()->suspend_property_changes();
2663 rv->region()->set_position(i->initial_position + dt);
2664 rv->region_changed(ARDOUR::Properties::position);
2667 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2668 Timecode::Time timecode;
2669 Timecode::Time timediff;
2671 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2672 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2673 snprintf (buf, sizeof (buf),
2674 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2675 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2676 , _("Video Start:"),
2677 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2679 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2681 show_verbose_cursor_text (buf);
2685 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2687 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2694 if (!movement_occurred || ! _editor->session()) {
2698 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2700 _editor->begin_reversible_command (_("Move Video"));
2702 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2703 ARDOUR_UI::instance()->video_timeline->save_undo();
2704 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2705 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2707 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2708 i->view->drag_end();
2709 i->view->region()->resume_property_changes ();
2711 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2714 _editor->session()->maybe_update_session_range(
2715 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2716 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2720 _editor->commit_reversible_command ();
2724 VideoTimeLineDrag::aborted (bool)
2726 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2729 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2730 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2732 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2733 i->view->region()->resume_property_changes ();
2734 i->view->region()->set_position(i->initial_position);
2738 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2739 : RegionDrag (e, i, p, v)
2740 , _operation (StartTrim)
2741 , _preserve_fade_anchor (preserve_fade_anchor)
2742 , _jump_position_when_done (false)
2744 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2748 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2751 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2752 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2754 if (tv && tv->is_track()) {
2755 speed = tv->track()->speed();
2758 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2759 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2760 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2762 framepos_t const pf = adjusted_current_frame (event);
2763 setup_snap_delta (region_start);
2765 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2766 /* Move the contents of the region around without changing the region bounds */
2767 _operation = ContentsTrim;
2768 Drag::start_grab (event, _editor->cursors()->trimmer);
2770 /* These will get overridden for a point trim.*/
2771 if (pf < (region_start + region_length/2)) {
2772 /* closer to front */
2773 _operation = StartTrim;
2774 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2775 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2777 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2781 _operation = EndTrim;
2782 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2783 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2785 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2789 /* jump trim disabled for now
2790 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2791 _jump_position_when_done = true;
2795 switch (_operation) {
2797 show_verbose_cursor_time (region_start);
2798 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2799 i->view->trim_front_starting ();
2803 show_verbose_cursor_duration (region_start, region_end);
2806 show_verbose_cursor_time (pf);
2810 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2811 i->view->region()->suspend_property_changes ();
2816 TrimDrag::motion (GdkEvent* event, bool first_move)
2818 RegionView* rv = _primary;
2821 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2822 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2823 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2824 frameoffset_t frame_delta = 0;
2826 if (tv && tv->is_track()) {
2827 speed = tv->track()->speed();
2829 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2830 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2836 switch (_operation) {
2838 trim_type = "Region start trim";
2841 trim_type = "Region end trim";
2844 trim_type = "Region content trim";
2851 _editor->begin_reversible_command (trim_type);
2853 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2854 RegionView* rv = i->view;
2855 rv->enable_display (false);
2856 rv->region()->playlist()->clear_owned_changes ();
2858 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2861 arv->temporarily_hide_envelope ();
2865 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2866 insert_result = _editor->motion_frozen_playlists.insert (pl);
2868 if (insert_result.second) {
2874 bool non_overlap_trim = false;
2876 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2877 non_overlap_trim = true;
2880 /* contstrain trim to fade length */
2881 if (_preserve_fade_anchor) {
2882 switch (_operation) {
2884 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2885 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2887 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2888 if (ar->locked()) continue;
2889 framecnt_t len = ar->fade_in()->back()->when;
2890 if (len < dt) dt = min(dt, len);
2894 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2895 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2897 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2898 if (ar->locked()) continue;
2899 framecnt_t len = ar->fade_out()->back()->when;
2900 if (len < -dt) dt = max(dt, -len);
2909 switch (_operation) {
2911 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2912 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2913 if (changed && _preserve_fade_anchor) {
2914 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2916 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2917 framecnt_t len = ar->fade_in()->back()->when;
2918 framecnt_t diff = ar->first_frame() - i->initial_position;
2919 framepos_t new_length = len - diff;
2920 i->anchored_fade_length = min (ar->length(), new_length);
2921 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2922 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2929 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2930 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2931 if (changed && _preserve_fade_anchor) {
2932 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2934 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2935 framecnt_t len = ar->fade_out()->back()->when;
2936 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2937 framepos_t new_length = len + diff;
2938 i->anchored_fade_length = min (ar->length(), new_length);
2939 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2940 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2948 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2950 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2951 i->view->move_contents (frame_delta);
2957 switch (_operation) {
2959 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2962 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2965 // show_verbose_cursor_time (frame_delta);
2971 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2973 if (movement_occurred) {
2974 motion (event, false);
2976 if (_operation == StartTrim) {
2977 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2979 /* This must happen before the region's StatefulDiffCommand is created, as it may
2980 `correct' (ahem) the region's _start from being negative to being zero. It
2981 needs to be zero in the undo record.
2983 i->view->trim_front_ending ();
2985 if (_preserve_fade_anchor) {
2986 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2988 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2989 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2990 ar->set_fade_in_length(i->anchored_fade_length);
2991 ar->set_fade_in_active(true);
2994 if (_jump_position_when_done) {
2995 i->view->region()->set_position (i->initial_position);
2998 } else if (_operation == EndTrim) {
2999 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3000 if (_preserve_fade_anchor) {
3001 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3003 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3004 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3005 ar->set_fade_out_length(i->anchored_fade_length);
3006 ar->set_fade_out_active(true);
3009 if (_jump_position_when_done) {
3010 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3015 if (!_views.empty()) {
3016 if (_operation == StartTrim) {
3017 _editor->maybe_locate_with_edit_preroll(
3018 _views.begin()->view->region()->position());
3020 if (_operation == EndTrim) {
3021 _editor->maybe_locate_with_edit_preroll(
3022 _views.begin()->view->region()->position() +
3023 _views.begin()->view->region()->length());
3027 if (!_editor->selection->selected (_primary)) {
3028 _primary->thaw_after_trim ();
3031 set<boost::shared_ptr<Playlist> > diffed_playlists;
3033 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3034 i->view->thaw_after_trim ();
3035 i->view->enable_display (true);
3037 /* Trimming one region may affect others on the playlist, so we need
3038 to get undo Commands from the whole playlist rather than just the
3039 region. Use diffed_playlists to make sure we don't diff a given
3040 playlist more than once.
3042 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3043 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3044 vector<Command*> cmds;
3046 _editor->session()->add_commands (cmds);
3047 diffed_playlists.insert (p);
3052 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3056 _editor->motion_frozen_playlists.clear ();
3057 _editor->commit_reversible_command();
3060 /* no mouse movement */
3061 _editor->point_trim (event, adjusted_current_frame (event));
3064 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3065 if (_operation == StartTrim) {
3066 i->view->trim_front_ending ();
3069 i->view->region()->resume_property_changes ();
3074 TrimDrag::aborted (bool movement_occurred)
3076 /* Our motion method is changing model state, so use the Undo system
3077 to cancel. Perhaps not ideal, as this will leave an Undo point
3078 behind which may be slightly odd from the user's point of view.
3083 if (movement_occurred) {
3087 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3088 i->view->region()->resume_property_changes ();
3093 TrimDrag::setup_pointer_frame_offset ()
3095 list<DraggingView>::iterator i = _views.begin ();
3096 while (i != _views.end() && i->view != _primary) {
3100 if (i == _views.end()) {
3104 switch (_operation) {
3106 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3109 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3116 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3121 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3122 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3127 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3129 Drag::start_grab (event, cursor);
3130 show_verbose_cursor_time (adjusted_current_frame(event));
3134 MeterMarkerDrag::setup_pointer_frame_offset ()
3136 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3140 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3142 if (!_marker->meter().movable()) {
3148 // create a dummy marker for visual representation of moving the
3149 // section, because whether its a copy or not, we're going to
3150 // leave or lose the original marker (leave if its a copy; lose if its
3151 // not, because we'll remove it from the map).
3153 MeterSection section (_marker->meter());
3155 if (!section.movable()) {
3160 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3162 _marker = new MeterMarker (
3164 *_editor->meter_group,
3165 UIConfiguration::instance().color ("meter marker"),
3167 *new MeterSection (_marker->meter())
3170 /* use the new marker for the grab */
3171 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3174 TempoMap& map (_editor->session()->tempo_map());
3175 /* get current state */
3176 before_state = &map.get_state();
3177 /* remove the section while we drag it */
3178 map.remove_meter (section, true);
3182 framepos_t const pf = adjusted_current_frame (event);
3184 _marker->set_position (pf);
3185 show_verbose_cursor_time (pf);
3189 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3191 if (!movement_occurred) {
3192 if (was_double_click()) {
3193 _editor->edit_meter_marker (*_marker);
3198 if (!_marker->meter().movable()) {
3202 motion (event, false);
3204 Timecode::BBT_Time when;
3206 TempoMap& map (_editor->session()->tempo_map());
3207 map.bbt_time (last_pointer_frame(), when);
3209 if (_copy == true) {
3210 _editor->begin_reversible_command (_("copy meter mark"));
3211 XMLNode &before = map.get_state();
3212 map.add_meter (_marker->meter(), when);
3213 XMLNode &after = map.get_state();
3214 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3215 _editor->commit_reversible_command ();
3218 _editor->begin_reversible_command (_("move meter mark"));
3220 /* we removed it before, so add it back now */
3222 map.add_meter (_marker->meter(), when);
3223 XMLNode &after = map.get_state();
3224 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3225 _editor->commit_reversible_command ();
3228 // delete the dummy marker we used for visual representation while moving.
3229 // a new visual marker will show up automatically.
3234 MeterMarkerDrag::aborted (bool moved)
3236 _marker->set_position (_marker->meter().frame ());
3239 TempoMap& map (_editor->session()->tempo_map());
3240 /* we removed it before, so add it back now */
3241 map.add_meter (_marker->meter(), _marker->meter().frame());
3242 // delete the dummy marker we used for visual representation while moving.
3243 // a new visual marker will show up automatically.
3248 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3253 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3255 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3260 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3262 Drag::start_grab (event, cursor);
3263 show_verbose_cursor_time (adjusted_current_frame (event));
3267 TempoMarkerDrag::setup_pointer_frame_offset ()
3269 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3273 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3275 if (!_marker->tempo().movable()) {
3281 // create a dummy marker for visual representation of moving the
3282 // section, because whether its a copy or not, we're going to
3283 // leave or lose the original marker (leave if its a copy; lose if its
3284 // not, because we'll remove it from the map).
3286 // create a dummy marker for visual representation of moving the copy.
3287 // The actual copying is not done before we reach the finish callback.
3290 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3292 TempoSection section (_marker->tempo());
3294 _marker = new TempoMarker (
3296 *_editor->tempo_group,
3297 UIConfiguration::instance().color ("tempo marker"),
3299 *new TempoSection (_marker->tempo())
3302 /* use the new marker for the grab */
3303 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3306 TempoMap& map (_editor->session()->tempo_map());
3307 /* get current state */
3308 before_state = &map.get_state();
3309 /* remove the section while we drag it */
3310 map.remove_tempo (section, true);
3314 framepos_t const pf = adjusted_current_frame (event);
3315 _marker->set_position (pf);
3316 show_verbose_cursor_time (pf);
3320 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3322 if (!movement_occurred) {
3323 if (was_double_click()) {
3324 _editor->edit_tempo_marker (*_marker);
3329 if (!_marker->tempo().movable()) {
3333 motion (event, false);
3335 TempoMap& map (_editor->session()->tempo_map());
3336 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3337 Timecode::BBT_Time when;
3339 map.bbt_time (beat_time, when);
3341 if (_copy == true) {
3342 _editor->begin_reversible_command (_("copy tempo mark"));
3343 XMLNode &before = map.get_state();
3344 map.add_tempo (_marker->tempo(), when);
3345 XMLNode &after = map.get_state();
3346 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3347 _editor->commit_reversible_command ();
3350 _editor->begin_reversible_command (_("move tempo mark"));
3351 /* we removed it before, so add it back now */
3352 map.add_tempo (_marker->tempo(), when);
3353 XMLNode &after = map.get_state();
3354 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3355 _editor->commit_reversible_command ();
3358 // delete the dummy marker we used for visual representation while moving.
3359 // a new visual marker will show up automatically.
3364 TempoMarkerDrag::aborted (bool moved)
3366 _marker->set_position (_marker->tempo().frame());
3368 TempoMap& map (_editor->session()->tempo_map());
3369 /* we removed it before, so add it back now */
3370 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3371 // delete the dummy marker we used for visual representation while moving.
3372 // a new visual marker will show up automatically.
3377 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3378 : Drag (e, &c.track_canvas_item(), false)
3383 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3386 /** Do all the things we do when dragging the playhead to make it look as though
3387 * we have located, without actually doing the locate (because that would cause
3388 * the diskstream buffers to be refilled, which is too slow).
3391 CursorDrag::fake_locate (framepos_t t)
3393 if (_editor->session () == 0) {
3397 _editor->playhead_cursor->set_position (t);
3399 Session* s = _editor->session ();
3400 if (s->timecode_transmission_suspended ()) {
3401 framepos_t const f = _editor->playhead_cursor->current_frame ();
3402 /* This is asynchronous so it will be sent "now"
3404 s->send_mmc_locate (f);
3405 /* These are synchronous and will be sent during the next
3408 s->queue_full_time_code ();
3409 s->queue_song_position_pointer ();
3412 show_verbose_cursor_time (t);
3413 _editor->UpdateAllTransportClocks (t);
3417 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3419 Drag::start_grab (event, c);
3420 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3422 _grab_zoom = _editor->samples_per_pixel;
3424 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3426 _editor->snap_to_with_modifier (where, event);
3428 _editor->_dragging_playhead = true;
3430 Session* s = _editor->session ();
3432 /* grab the track canvas item as well */
3434 _cursor.track_canvas_item().grab();
3437 if (_was_rolling && _stop) {
3441 if (s->is_auditioning()) {
3442 s->cancel_audition ();
3446 if (AudioEngine::instance()->connected()) {
3448 /* do this only if we're the engine is connected
3449 * because otherwise this request will never be
3450 * serviced and we'll busy wait forever. likewise,
3451 * notice if we are disconnected while waiting for the
3452 * request to be serviced.
3455 s->request_suspend_timecode_transmission ();
3456 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3457 /* twiddle our thumbs */
3462 fake_locate (where - snap_delta (event->button.state));
3466 CursorDrag::motion (GdkEvent* event, bool)
3468 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3469 _editor->snap_to_with_modifier (where, event);
3470 if (where != last_pointer_frame()) {
3471 fake_locate (where - snap_delta (event->button.state));
3476 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3478 _editor->_dragging_playhead = false;
3480 _cursor.track_canvas_item().ungrab();
3482 if (!movement_occurred && _stop) {
3486 motion (event, false);
3488 Session* s = _editor->session ();
3490 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3491 _editor->_pending_locate_request = true;
3492 s->request_resume_timecode_transmission ();
3497 CursorDrag::aborted (bool)
3499 _cursor.track_canvas_item().ungrab();
3501 if (_editor->_dragging_playhead) {
3502 _editor->session()->request_resume_timecode_transmission ();
3503 _editor->_dragging_playhead = false;
3506 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3509 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3510 : RegionDrag (e, i, p, v)
3512 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3516 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3518 Drag::start_grab (event, cursor);
3520 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3521 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3522 setup_snap_delta (r->position ());
3524 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3528 FadeInDrag::setup_pointer_frame_offset ()
3530 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3531 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3532 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3536 FadeInDrag::motion (GdkEvent* event, bool)
3538 framecnt_t fade_length;
3540 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3541 _editor->snap_to_with_modifier (pos, event);
3542 pos -= snap_delta (event->button.state);
3544 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3546 if (pos < (region->position() + 64)) {
3547 fade_length = 64; // this should be a minimum defined somewhere
3548 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3549 fade_length = region->length() - region->fade_out()->back()->when - 1;
3551 fade_length = pos - region->position();
3554 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3556 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3562 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3565 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3569 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3571 if (!movement_occurred) {
3575 framecnt_t fade_length;
3576 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3577 _editor->snap_to_with_modifier (pos, event);
3578 pos -= snap_delta (event->button.state);
3580 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3582 if (pos < (region->position() + 64)) {
3583 fade_length = 64; // this should be a minimum defined somewhere
3584 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3585 fade_length = region->length() - region->fade_out()->back()->when - 1;
3587 fade_length = pos - region->position();
3590 bool in_command = false;
3592 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3594 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3600 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3601 XMLNode &before = alist->get_state();
3603 tmp->audio_region()->set_fade_in_length (fade_length);
3604 tmp->audio_region()->set_fade_in_active (true);
3607 _editor->begin_reversible_command (_("change fade in length"));
3610 XMLNode &after = alist->get_state();
3611 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3615 _editor->commit_reversible_command ();
3620 FadeInDrag::aborted (bool)
3622 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3623 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3629 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3633 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3634 : RegionDrag (e, i, p, v)
3636 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3640 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3642 Drag::start_grab (event, cursor);
3644 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3645 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3646 setup_snap_delta (r->last_frame ());
3648 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3652 FadeOutDrag::setup_pointer_frame_offset ()
3654 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3655 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3656 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3660 FadeOutDrag::motion (GdkEvent* event, bool)
3662 framecnt_t fade_length;
3664 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3665 _editor->snap_to_with_modifier (pos, event);
3666 pos -= snap_delta (event->button.state);
3668 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3670 if (pos > (region->last_frame() - 64)) {
3671 fade_length = 64; // this should really be a minimum fade defined somewhere
3672 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3673 fade_length = region->length() - region->fade_in()->back()->when - 1;
3675 fade_length = region->last_frame() - pos;
3678 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3680 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3686 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3689 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3693 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3695 if (!movement_occurred) {
3699 framecnt_t fade_length;
3701 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3702 _editor->snap_to_with_modifier (pos, event);
3703 pos -= snap_delta (event->button.state);
3705 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3707 if (pos > (region->last_frame() - 64)) {
3708 fade_length = 64; // this should really be a minimum fade defined somewhere
3709 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3710 fade_length = region->length() - region->fade_in()->back()->when - 1;
3712 fade_length = region->last_frame() - pos;
3715 bool in_command = false;
3717 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3719 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3725 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3726 XMLNode &before = alist->get_state();
3728 tmp->audio_region()->set_fade_out_length (fade_length);
3729 tmp->audio_region()->set_fade_out_active (true);
3732 _editor->begin_reversible_command (_("change fade out length"));
3735 XMLNode &after = alist->get_state();
3736 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3740 _editor->commit_reversible_command ();
3745 FadeOutDrag::aborted (bool)
3747 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3748 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3754 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3758 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3760 , _selection_changed (false)
3762 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3764 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3767 _points.push_back (ArdourCanvas::Duple (0, 0));
3768 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3771 MarkerDrag::~MarkerDrag ()
3773 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3778 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3780 location = new Location (*l);
3781 markers.push_back (m);
3786 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3788 Drag::start_grab (event, cursor);
3792 Location *location = _editor->find_location_from_marker (_marker, is_start);
3793 _editor->_dragging_edit_point = true;
3795 update_item (location);
3797 // _drag_line->show();
3798 // _line->raise_to_top();
3801 show_verbose_cursor_time (location->start());
3803 show_verbose_cursor_time (location->end());
3805 setup_snap_delta (is_start ? location->start() : location->end());
3807 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3810 case Selection::Toggle:
3811 /* we toggle on the button release */
3813 case Selection::Set:
3814 if (!_editor->selection->selected (_marker)) {
3815 _editor->selection->set (_marker);
3816 _selection_changed = true;
3819 case Selection::Extend:
3821 Locations::LocationList ll;
3822 list<ArdourMarker*> to_add;
3824 _editor->selection->markers.range (s, e);
3825 s = min (_marker->position(), s);
3826 e = max (_marker->position(), e);
3829 if (e < max_framepos) {
3832 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3833 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3834 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3837 to_add.push_back (lm->start);
3840 to_add.push_back (lm->end);
3844 if (!to_add.empty()) {
3845 _editor->selection->add (to_add);
3846 _selection_changed = true;
3850 case Selection::Add:
3851 _editor->selection->add (_marker);
3852 _selection_changed = true;
3857 /* Set up copies for us to manipulate during the drag
3860 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3862 Location* l = _editor->find_location_from_marker (*i, is_start);
3869 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3871 /* range: check that the other end of the range isn't
3874 CopiedLocationInfo::iterator x;
3875 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3876 if (*(*x).location == *l) {
3880 if (x == _copied_locations.end()) {
3881 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3883 (*x).markers.push_back (*i);
3884 (*x).move_both = true;
3892 MarkerDrag::setup_pointer_frame_offset ()
3895 Location *location = _editor->find_location_from_marker (_marker, is_start);
3896 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3900 MarkerDrag::motion (GdkEvent* event, bool)
3902 framecnt_t f_delta = 0;
3904 bool move_both = false;
3905 Location *real_location;
3906 Location *copy_location = 0;
3907 framecnt_t const sd = snap_delta (event->button.state);
3909 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
3910 framepos_t next = newframe;
3912 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3916 CopiedLocationInfo::iterator x;
3918 /* find the marker we're dragging, and compute the delta */
3920 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3922 copy_location = (*x).location;
3924 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3926 /* this marker is represented by this
3927 * CopiedLocationMarkerInfo
3930 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3935 if (real_location->is_mark()) {
3936 f_delta = newframe - copy_location->start();
3940 switch (_marker->type()) {
3941 case ArdourMarker::SessionStart:
3942 case ArdourMarker::RangeStart:
3943 case ArdourMarker::LoopStart:
3944 case ArdourMarker::PunchIn:
3945 f_delta = newframe - copy_location->start();
3948 case ArdourMarker::SessionEnd:
3949 case ArdourMarker::RangeEnd:
3950 case ArdourMarker::LoopEnd:
3951 case ArdourMarker::PunchOut:
3952 f_delta = newframe - copy_location->end();
3955 /* what kind of marker is this ? */
3964 if (x == _copied_locations.end()) {
3965 /* hmm, impossible - we didn't find the dragged marker */
3969 /* now move them all */
3971 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3973 copy_location = x->location;
3975 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3979 if (real_location->locked()) {
3983 if (copy_location->is_mark()) {
3987 copy_location->set_start (copy_location->start() + f_delta);
3991 framepos_t new_start = copy_location->start() + f_delta;
3992 framepos_t new_end = copy_location->end() + f_delta;
3994 if (is_start) { // start-of-range marker
3996 if (move_both || (*x).move_both) {
3997 copy_location->set_start (new_start);
3998 copy_location->set_end (new_end);
3999 } else if (new_start < copy_location->end()) {
4000 copy_location->set_start (new_start);
4001 } else if (newframe > 0) {
4002 //_editor->snap_to (next, RoundUpAlways, true);
4003 copy_location->set_end (next);
4004 copy_location->set_start (newframe);
4007 } else { // end marker
4009 if (move_both || (*x).move_both) {
4010 copy_location->set_end (new_end);
4011 copy_location->set_start (new_start);
4012 } else if (new_end > copy_location->start()) {
4013 copy_location->set_end (new_end);
4014 } else if (newframe > 0) {
4015 //_editor->snap_to (next, RoundDownAlways, true);
4016 copy_location->set_start (next);
4017 copy_location->set_end (newframe);
4022 update_item (copy_location);
4024 /* now lookup the actual GUI items used to display this
4025 * location and move them to wherever the copy of the location
4026 * is now. This means that the logic in ARDOUR::Location is
4027 * still enforced, even though we are not (yet) modifying
4028 * the real Location itself.
4031 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4034 lm->set_position (copy_location->start(), copy_location->end());
4039 assert (!_copied_locations.empty());
4041 show_verbose_cursor_time (newframe);
4045 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4047 if (!movement_occurred) {
4049 if (was_double_click()) {
4050 _editor->rename_marker (_marker);
4054 /* just a click, do nothing but finish
4055 off the selection process
4058 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4060 case Selection::Set:
4061 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4062 _editor->selection->set (_marker);
4063 _selection_changed = true;
4067 case Selection::Toggle:
4068 /* we toggle on the button release, click only */
4069 _editor->selection->toggle (_marker);
4070 _selection_changed = true;
4074 case Selection::Extend:
4075 case Selection::Add:
4079 if (_selection_changed) {
4080 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4081 _editor->commit_reversible_selection_op();
4087 _editor->_dragging_edit_point = false;
4089 XMLNode &before = _editor->session()->locations()->get_state();
4090 bool in_command = false;
4092 MarkerSelection::iterator i;
4093 CopiedLocationInfo::iterator x;
4096 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4097 x != _copied_locations.end() && i != _editor->selection->markers.end();
4100 Location * location = _editor->find_location_from_marker (*i, is_start);
4104 if (location->locked()) {
4108 _editor->begin_reversible_command ( _("move marker") );
4111 if (location->is_mark()) {
4112 location->set_start (((*x).location)->start());
4114 location->set (((*x).location)->start(), ((*x).location)->end());
4120 XMLNode &after = _editor->session()->locations()->get_state();
4121 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4122 _editor->commit_reversible_command ();
4127 MarkerDrag::aborted (bool movement_occured)
4129 if (!movement_occured) {
4133 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4135 /* move all markers to their original location */
4138 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4141 Location * location = _editor->find_location_from_marker (*m, is_start);
4144 (*m)->set_position (is_start ? location->start() : location->end());
4151 MarkerDrag::update_item (Location*)
4156 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4158 , _fixed_grab_x (0.0)
4159 , _fixed_grab_y (0.0)
4160 , _cumulative_x_drag (0.0)
4161 , _cumulative_y_drag (0.0)
4165 if (_zero_gain_fraction < 0.0) {
4166 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4169 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4171 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4177 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4179 Drag::start_grab (event, _editor->cursors()->fader);
4181 // start the grab at the center of the control point so
4182 // the point doesn't 'jump' to the mouse after the first drag
4183 _fixed_grab_x = _point->get_x();
4184 _fixed_grab_y = _point->get_y();
4186 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4187 setup_snap_delta (pos);
4189 float const fraction = 1 - (_point->get_y() / _point->line().height());
4190 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4192 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4194 if (!_point->can_slide ()) {
4195 _x_constrained = true;
4200 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4202 double dx = _drags->current_pointer_x() - last_pointer_x();
4203 double dy = current_pointer_y() - last_pointer_y();
4204 bool need_snap = true;
4206 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4212 /* coordinate in pixels relative to the start of the region (for region-based automation)
4213 or track (for track-based automation) */
4214 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4215 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4217 // calculate zero crossing point. back off by .01 to stay on the
4218 // positive side of zero
4219 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4221 if (_x_constrained) {
4224 if (_y_constrained) {
4228 _cumulative_x_drag = cx - _fixed_grab_x;
4229 _cumulative_y_drag = cy - _fixed_grab_y;
4233 cy = min ((double) _point->line().height(), cy);
4235 // make sure we hit zero when passing through
4236 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4240 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4242 if (!_x_constrained && need_snap) {
4243 _editor->snap_to_with_modifier (cx_frames, event);
4246 cx_frames -= snap_delta (event->button.state);
4247 cx_frames = min (cx_frames, _point->line().maximum_time());
4249 float const fraction = 1.0 - (cy / _point->line().height());
4252 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4253 _editor->begin_reversible_command (_("automation event move"));
4254 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4256 pair<double, float> result;
4257 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4259 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4263 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4265 if (!movement_occurred) {
4268 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4269 _editor->reset_point_selection ();
4273 _point->line().end_drag (_pushing, _final_index);
4274 _editor->commit_reversible_command ();
4279 ControlPointDrag::aborted (bool)
4281 _point->line().reset ();
4285 ControlPointDrag::active (Editing::MouseMode m)
4287 if (m == Editing::MouseDraw) {
4288 /* always active in mouse draw */
4292 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4293 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4296 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4299 , _fixed_grab_x (0.0)
4300 , _fixed_grab_y (0.0)
4301 , _cumulative_y_drag (0)
4305 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4309 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4311 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4314 _item = &_line->grab_item ();
4316 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4317 origin, and ditto for y.
4320 double mx = event->button.x;
4321 double my = event->button.y;
4323 _line->parent_group().canvas_to_item (mx, my);
4325 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4327 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4328 /* no adjacent points */
4332 Drag::start_grab (event, _editor->cursors()->fader);
4334 /* store grab start in parent frame */
4335 double const bx = _line->nth (_before)->get_x();
4336 double const ax = _line->nth (_after)->get_x();
4337 double const click_ratio = (mx - bx) / (ax - bx);
4339 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4344 double fraction = 1.0 - (cy / _line->height());
4346 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4350 LineDrag::motion (GdkEvent* event, bool first_move)
4352 double dy = current_pointer_y() - last_pointer_y();
4354 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4358 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4360 _cumulative_y_drag = cy - _fixed_grab_y;
4363 cy = min ((double) _line->height(), cy);
4365 double const fraction = 1.0 - (cy / _line->height());
4369 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4371 _editor->begin_reversible_command (_("automation range move"));
4372 _line->start_drag_line (_before, _after, initial_fraction);
4375 /* we are ignoring x position for this drag, so we can just pass in anything */
4376 pair<double, float> result;
4378 result = _line->drag_motion (0, fraction, true, false, ignored);
4379 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4383 LineDrag::finished (GdkEvent* event, bool movement_occured)
4385 if (movement_occured) {
4386 motion (event, false);
4387 _line->end_drag (false, 0);
4388 _editor->commit_reversible_command ();
4390 /* add a new control point on the line */
4392 AutomationTimeAxisView* atv;
4394 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4395 framepos_t where = _editor->canvas_event_sample (event, 0, 0);
4397 atv->add_automation_event (event, where, event->button.y, false);
4398 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4399 AudioRegionView* arv;
4401 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4402 arv->add_gain_point_event (arv->get_canvas_group (), event, false);
4409 LineDrag::aborted (bool)
4414 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4418 _region_view_grab_x (0.0),
4419 _cumulative_x_drag (0),
4423 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4427 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4429 Drag::start_grab (event);
4431 _line = reinterpret_cast<Line*> (_item);
4434 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4436 double cx = event->button.x;
4437 double cy = event->button.y;
4439 _item->parent()->canvas_to_item (cx, cy);
4441 /* store grab start in parent frame */
4442 _region_view_grab_x = cx;
4444 _before = *(float*) _item->get_data ("position");
4446 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4448 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4452 FeatureLineDrag::motion (GdkEvent*, bool)
4454 double dx = _drags->current_pointer_x() - last_pointer_x();
4456 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4458 _cumulative_x_drag += dx;
4460 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4469 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4471 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4473 float *pos = new float;
4476 _line->set_data ("position", pos);
4482 FeatureLineDrag::finished (GdkEvent*, bool)
4484 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4485 _arv->update_transient(_before, _before);
4489 FeatureLineDrag::aborted (bool)
4494 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4496 , _vertical_only (false)
4498 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4502 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4504 Drag::start_grab (event);
4505 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4509 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4516 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4518 framepos_t grab = grab_frame ();
4519 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4520 _editor->snap_to_with_modifier (grab, event);
4522 grab = raw_grab_frame ();
4525 /* base start and end on initial click position */
4535 if (current_pointer_y() < grab_y()) {
4536 y1 = current_pointer_y();
4539 y2 = current_pointer_y();
4543 if (start != end || y1 != y2) {
4545 double x1 = _editor->sample_to_pixel (start);
4546 double x2 = _editor->sample_to_pixel (end);
4547 const double min_dimension = 2.0;
4549 if (_vertical_only) {
4550 /* fixed 10 pixel width */
4554 x2 = min (x1 - min_dimension, x2);
4556 x2 = max (x1 + min_dimension, x2);
4561 y2 = min (y1 - min_dimension, y2);
4563 y2 = max (y1 + min_dimension, y2);
4566 /* translate rect into item space and set */
4568 ArdourCanvas::Rect r (x1, y1, x2, y2);
4570 /* this drag is a _trackview_only == true drag, so the y1 and
4571 * y2 (computed using current_pointer_y() and grab_y()) will be
4572 * relative to the top of the trackview group). The
4573 * rubberband rect has the same parent/scroll offset as the
4574 * the trackview group, so we can use the "r" rect directly
4575 * to set the shape of the rubberband.
4578 _editor->rubberband_rect->set (r);
4579 _editor->rubberband_rect->show();
4580 _editor->rubberband_rect->raise_to_top();
4582 show_verbose_cursor_time (pf);
4584 do_select_things (event, true);
4589 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4593 framepos_t grab = grab_frame ();
4594 framepos_t lpf = last_pointer_frame ();
4596 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4597 grab = raw_grab_frame ();
4598 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4612 if (current_pointer_y() < grab_y()) {
4613 y1 = current_pointer_y();
4616 y2 = current_pointer_y();
4620 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4624 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4626 if (movement_occurred) {
4628 motion (event, false);
4629 do_select_things (event, false);
4635 bool do_deselect = true;
4636 MidiTimeAxisView* mtv;
4638 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4640 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4641 /* nothing selected */
4642 add_midi_region (mtv);
4643 do_deselect = false;
4647 /* do not deselect if Primary or Tertiary (toggle-select or
4648 * extend-select are pressed.
4651 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4652 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4659 _editor->rubberband_rect->hide();
4663 RubberbandSelectDrag::aborted (bool)
4665 _editor->rubberband_rect->hide ();
4668 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4669 : RegionDrag (e, i, p, v)
4671 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4675 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4677 Drag::start_grab (event, cursor);
4679 _editor->get_selection().add (_primary);
4681 framepos_t where = _primary->region()->position();
4682 setup_snap_delta (where);
4684 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4688 TimeFXDrag::motion (GdkEvent* event, bool)
4690 RegionView* rv = _primary;
4691 StreamView* cv = rv->get_time_axis_view().view ();
4693 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4694 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4695 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4696 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4697 _editor->snap_to_with_modifier (pf, event);
4698 pf -= snap_delta (event->button.state);
4700 if (pf > rv->region()->position()) {
4701 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4704 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4708 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4710 _primary->get_time_axis_view().hide_timestretch ();
4712 if (!movement_occurred) {
4716 if (last_pointer_frame() < _primary->region()->position()) {
4717 /* backwards drag of the left edge - not usable */
4721 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4723 float percentage = (double) newlen / (double) _primary->region()->length();
4725 #ifndef USE_RUBBERBAND
4726 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4727 if (_primary->region()->data_type() == DataType::AUDIO) {
4728 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4732 if (!_editor->get_selection().regions.empty()) {
4733 /* primary will already be included in the selection, and edit
4734 group shared editing will propagate selection across
4735 equivalent regions, so just use the current region
4739 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4740 error << _("An error occurred while executing time stretch operation") << endmsg;
4746 TimeFXDrag::aborted (bool)
4748 _primary->get_time_axis_view().hide_timestretch ();
4751 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4754 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4758 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4760 Drag::start_grab (event);
4764 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4766 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4770 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4772 if (movement_occurred && _editor->session()) {
4773 /* make sure we stop */
4774 _editor->session()->request_transport_speed (0.0);
4779 ScrubDrag::aborted (bool)
4784 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4788 , _time_selection_at_start (!_editor->get_selection().time.empty())
4790 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4792 if (_time_selection_at_start) {
4793 start_at_start = _editor->get_selection().time.start();
4794 end_at_start = _editor->get_selection().time.end_frame();
4799 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4801 if (_editor->session() == 0) {
4805 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4807 switch (_operation) {
4808 case CreateSelection:
4809 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4814 cursor = _editor->cursors()->selector;
4815 Drag::start_grab (event, cursor);
4818 case SelectionStartTrim:
4819 if (_editor->clicked_axisview) {
4820 _editor->clicked_axisview->order_selection_trims (_item, true);
4822 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4825 case SelectionEndTrim:
4826 if (_editor->clicked_axisview) {
4827 _editor->clicked_axisview->order_selection_trims (_item, false);
4829 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4833 Drag::start_grab (event, cursor);
4836 case SelectionExtend:
4837 Drag::start_grab (event, cursor);
4841 if (_operation == SelectionMove) {
4842 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4844 show_verbose_cursor_time (adjusted_current_frame (event));
4849 SelectionDrag::setup_pointer_frame_offset ()
4851 switch (_operation) {
4852 case CreateSelection:
4853 _pointer_frame_offset = 0;
4856 case SelectionStartTrim:
4858 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4861 case SelectionEndTrim:
4862 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4865 case SelectionExtend:
4871 SelectionDrag::motion (GdkEvent* event, bool first_move)
4873 framepos_t start = 0;
4875 framecnt_t length = 0;
4876 framecnt_t distance = 0;
4878 framepos_t const pending_position = adjusted_current_frame (event);
4880 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4884 switch (_operation) {
4885 case CreateSelection:
4887 framepos_t grab = grab_frame ();
4890 grab = adjusted_current_frame (event, false);
4891 if (grab < pending_position) {
4892 _editor->snap_to (grab, RoundDownMaybe);
4894 _editor->snap_to (grab, RoundUpMaybe);
4898 if (pending_position < grab) {
4899 start = pending_position;
4902 end = pending_position;
4906 /* first drag: Either add to the selection
4907 or create a new selection
4914 /* adding to the selection */
4915 _editor->set_selected_track_as_side_effect (Selection::Add);
4916 _editor->clicked_selection = _editor->selection->add (start, end);
4923 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4924 _editor->set_selected_track_as_side_effect (Selection::Set);
4927 _editor->clicked_selection = _editor->selection->set (start, end);
4931 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4932 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4933 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4935 _editor->selection->add (atest);
4939 /* select all tracks within the rectangle that we've marked out so far */
4940 TrackViewList new_selection;
4941 TrackViewList& all_tracks (_editor->track_views);
4943 ArdourCanvas::Coord const top = grab_y();
4944 ArdourCanvas::Coord const bottom = current_pointer_y();
4946 if (top >= 0 && bottom >= 0) {
4948 //first, find the tracks that are covered in the y range selection
4949 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4950 if ((*i)->covered_by_y_range (top, bottom)) {
4951 new_selection.push_back (*i);
4955 //now find any tracks that are GROUPED with the tracks we selected
4956 TrackViewList grouped_add = new_selection;
4957 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4958 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4959 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4960 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4961 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4962 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4963 grouped_add.push_back (*j);
4968 //now compare our list with the current selection, and add or remove as necessary
4969 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4970 TrackViewList tracks_to_add;
4971 TrackViewList tracks_to_remove;
4972 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4973 if ( !_editor->selection->tracks.contains ( *i ) )
4974 tracks_to_add.push_back ( *i );
4975 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4976 if ( !grouped_add.contains ( *i ) )
4977 tracks_to_remove.push_back ( *i );
4978 _editor->selection->add(tracks_to_add);
4979 _editor->selection->remove(tracks_to_remove);
4985 case SelectionStartTrim:
4987 end = _editor->selection->time[_editor->clicked_selection].end;
4989 if (pending_position > end) {
4992 start = pending_position;
4996 case SelectionEndTrim:
4998 start = _editor->selection->time[_editor->clicked_selection].start;
5000 if (pending_position < start) {
5003 end = pending_position;
5010 start = _editor->selection->time[_editor->clicked_selection].start;
5011 end = _editor->selection->time[_editor->clicked_selection].end;
5013 length = end - start;
5014 distance = pending_position - start;
5015 start = pending_position;
5016 _editor->snap_to (start);
5018 end = start + length;
5022 case SelectionExtend:
5027 switch (_operation) {
5029 if (_time_selection_at_start) {
5030 _editor->selection->move_time (distance);
5034 _editor->selection->replace (_editor->clicked_selection, start, end);
5038 if (_operation == SelectionMove) {
5039 show_verbose_cursor_time(start);
5041 show_verbose_cursor_time(pending_position);
5046 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5048 Session* s = _editor->session();
5050 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5051 if (movement_occurred) {
5052 motion (event, false);
5053 /* XXX this is not object-oriented programming at all. ick */
5054 if (_editor->selection->time.consolidate()) {
5055 _editor->selection->TimeChanged ();
5058 /* XXX what if its a music time selection? */
5060 if (s->get_play_range() && s->transport_rolling()) {
5061 s->request_play_range (&_editor->selection->time, true);
5062 } else if (!s->config.get_external_sync()) {
5063 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5064 if (_operation == SelectionEndTrim)
5065 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5067 s->request_locate (_editor->get_selection().time.start());
5071 if (_editor->get_selection().time.length() != 0) {
5072 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5074 s->clear_range_selection ();
5079 /* just a click, no pointer movement.
5082 if (_operation == SelectionExtend) {
5083 if (_time_selection_at_start) {
5084 framepos_t pos = adjusted_current_frame (event, false);
5085 framepos_t start = min (pos, start_at_start);
5086 framepos_t end = max (pos, end_at_start);
5087 _editor->selection->set (start, end);
5090 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5091 if (_editor->clicked_selection) {
5092 _editor->selection->remove (_editor->clicked_selection);
5095 if (!_editor->clicked_selection) {
5096 _editor->selection->clear_time();
5101 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5102 _editor->selection->set (_editor->clicked_axisview);
5105 if (s && s->get_play_range () && s->transport_rolling()) {
5106 s->request_stop (false, false);
5111 _editor->stop_canvas_autoscroll ();
5112 _editor->clicked_selection = 0;
5113 _editor->commit_reversible_selection_op ();
5117 SelectionDrag::aborted (bool)
5122 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5123 : Drag (e, i, false),
5127 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5129 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5130 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5131 physical_screen_height (_editor->get_window())));
5132 _drag_rect->hide ();
5134 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5135 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5138 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5140 /* normal canvas items will be cleaned up when their parent group is deleted. But
5141 this item is created as the child of a long-lived parent group, and so we
5142 need to explicitly delete it.
5148 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5150 if (_editor->session() == 0) {
5154 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5156 if (!_editor->temp_location) {
5157 _editor->temp_location = new Location (*_editor->session());
5160 switch (_operation) {
5161 case CreateSkipMarker:
5162 case CreateRangeMarker:
5163 case CreateTransportMarker:
5164 case CreateCDMarker:
5166 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5171 cursor = _editor->cursors()->selector;
5175 Drag::start_grab (event, cursor);
5177 show_verbose_cursor_time (adjusted_current_frame (event));
5181 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5183 framepos_t start = 0;
5185 ArdourCanvas::Rectangle *crect;
5187 switch (_operation) {
5188 case CreateSkipMarker:
5189 crect = _editor->range_bar_drag_rect;
5191 case CreateRangeMarker:
5192 crect = _editor->range_bar_drag_rect;
5194 case CreateTransportMarker:
5195 crect = _editor->transport_bar_drag_rect;
5197 case CreateCDMarker:
5198 crect = _editor->cd_marker_bar_drag_rect;
5201 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5206 framepos_t const pf = adjusted_current_frame (event);
5208 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5209 framepos_t grab = grab_frame ();
5210 _editor->snap_to (grab);
5212 if (pf < grab_frame()) {
5220 /* first drag: Either add to the selection
5221 or create a new selection.
5226 _editor->temp_location->set (start, end);
5230 update_item (_editor->temp_location);
5232 //_drag_rect->raise_to_top();
5238 _editor->temp_location->set (start, end);
5240 double x1 = _editor->sample_to_pixel (start);
5241 double x2 = _editor->sample_to_pixel (end);
5245 update_item (_editor->temp_location);
5248 show_verbose_cursor_time (pf);
5253 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5255 Location * newloc = 0;
5259 if (movement_occurred) {
5260 motion (event, false);
5263 switch (_operation) {
5264 case CreateSkipMarker:
5265 case CreateRangeMarker:
5266 case CreateCDMarker:
5268 XMLNode &before = _editor->session()->locations()->get_state();
5269 if (_operation == CreateSkipMarker) {
5270 _editor->begin_reversible_command (_("new skip marker"));
5271 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5272 flags = Location::IsRangeMarker | Location::IsSkip;
5273 _editor->range_bar_drag_rect->hide();
5274 } else if (_operation == CreateCDMarker) {
5275 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5276 _editor->begin_reversible_command (_("new CD marker"));
5277 flags = Location::IsRangeMarker | Location::IsCDMarker;
5278 _editor->cd_marker_bar_drag_rect->hide();
5280 _editor->begin_reversible_command (_("new skip marker"));
5281 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5282 flags = Location::IsRangeMarker;
5283 _editor->range_bar_drag_rect->hide();
5285 newloc = new Location (
5286 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5289 _editor->session()->locations()->add (newloc, true);
5290 XMLNode &after = _editor->session()->locations()->get_state();
5291 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5292 _editor->commit_reversible_command ();
5296 case CreateTransportMarker:
5297 // popup menu to pick loop or punch
5298 _editor->new_transport_marker_context_menu (&event->button, _item);
5304 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5306 if (_operation == CreateTransportMarker) {
5308 /* didn't drag, so just locate */
5310 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5312 } else if (_operation == CreateCDMarker) {
5314 /* didn't drag, but mark is already created so do
5317 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5322 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5324 if (end == max_framepos) {
5325 end = _editor->session()->current_end_frame ();
5328 if (start == max_framepos) {
5329 start = _editor->session()->current_start_frame ();
5332 switch (_editor->mouse_mode) {
5334 /* find the two markers on either side and then make the selection from it */
5335 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5339 /* find the two markers on either side of the click and make the range out of it */
5340 _editor->selection->set (start, end);
5349 _editor->stop_canvas_autoscroll ();
5353 RangeMarkerBarDrag::aborted (bool movement_occured)
5355 if (movement_occured) {
5356 _drag_rect->hide ();
5361 RangeMarkerBarDrag::update_item (Location* location)
5363 double const x1 = _editor->sample_to_pixel (location->start());
5364 double const x2 = _editor->sample_to_pixel (location->end());
5366 _drag_rect->set_x0 (x1);
5367 _drag_rect->set_x1 (x2);
5370 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5372 , _cumulative_dx (0)
5373 , _cumulative_dy (0)
5374 , _was_selected (false)
5376 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5378 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5380 _region = &_primary->region_view ();
5381 _note_height = _region->midi_stream_view()->note_height ();
5385 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5387 Drag::start_grab (event);
5388 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5390 if (!(_was_selected = _primary->selected())) {
5392 /* tertiary-click means extend selection - we'll do that on button release,
5393 so don't add it here, because otherwise we make it hard to figure
5394 out the "extend-to" range.
5397 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5400 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5403 _region->note_selected (_primary, true);
5405 _editor->get_selection().clear_points();
5406 _region->unique_select (_primary);
5412 /** @return Current total drag x change in frames */
5414 NoteDrag::total_dx (const guint state) const
5417 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5419 /* primary note time */
5420 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5422 /* new time of the primary note in session frames */
5423 frameoffset_t st = n + dx + snap_delta (state);
5425 framepos_t const rp = _region->region()->position ();
5427 /* prevent the note being dragged earlier than the region's position */
5430 /* possibly snap and return corresponding delta */
5434 if (ArdourKeyboard::indicates_snap (state)) {
5435 if (_editor->snap_mode () != SnapOff) {
5439 if (_editor->snap_mode () == SnapOff) {
5441 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5442 if (ArdourKeyboard::indicates_snap_delta (state)) {
5450 ret = _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5452 ret = st - n - snap_delta (state);
5457 /** @return Current total drag y change in note number */
5459 NoteDrag::total_dy () const
5461 MidiStreamView* msv = _region->midi_stream_view ();
5462 double const y = _region->midi_view()->y_position ();
5463 /* new current note */
5464 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5466 n = max (msv->lowest_note(), n);
5467 n = min (msv->highest_note(), n);
5468 /* and work out delta */
5469 return n - msv->y_to_note (grab_y() - y);
5473 NoteDrag::motion (GdkEvent * event, bool)
5475 /* Total change in x and y since the start of the drag */
5476 frameoffset_t const dx = total_dx (event->button.state);
5477 int8_t const dy = total_dy ();
5479 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5480 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5481 double const tdy = -dy * _note_height - _cumulative_dy;
5484 _cumulative_dx += tdx;
5485 _cumulative_dy += tdy;
5487 int8_t note_delta = total_dy();
5489 _region->move_selection (tdx, tdy, note_delta);
5491 /* the new note value may be the same as the old one, but we
5492 * don't know what that means because the selection may have
5493 * involved more than one note and we might be doing something
5494 * odd with them. so show the note value anyway, always.
5498 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5500 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5501 (int) floor ((double)new_note));
5503 show_verbose_cursor_text (buf);
5508 NoteDrag::finished (GdkEvent* ev, bool moved)
5511 /* no motion - select note */
5513 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5514 _editor->current_mouse_mode() == Editing::MouseDraw) {
5516 bool changed = false;
5518 if (_was_selected) {
5519 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5521 _region->note_deselected (_primary);
5524 _editor->get_selection().clear_points();
5525 _region->unique_select (_primary);
5529 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5530 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5532 if (!extend && !add && _region->selection_size() > 1) {
5533 _editor->get_selection().clear_points();
5534 _region->unique_select (_primary);
5536 } else if (extend) {
5537 _region->note_selected (_primary, true, true);
5540 /* it was added during button press */
5547 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5548 _editor->commit_reversible_selection_op();
5552 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5557 NoteDrag::aborted (bool)
5562 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5563 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5564 : Drag (editor, atv->base_item ())
5566 , _y_origin (atv->y_position())
5567 , _nothing_to_drag (false)
5569 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5570 setup (atv->lines ());
5573 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5574 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5575 : Drag (editor, rv->get_canvas_group ())
5577 , _y_origin (rv->get_time_axis_view().y_position())
5578 , _nothing_to_drag (false)
5581 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5583 list<boost::shared_ptr<AutomationLine> > lines;
5585 AudioRegionView* audio_view;
5586 AutomationRegionView* automation_view;
5587 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5588 lines.push_back (audio_view->get_gain_line ());
5589 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5590 lines.push_back (automation_view->line ());
5593 error << _("Automation range drag created for invalid region type") << endmsg;
5599 /** @param lines AutomationLines to drag.
5600 * @param offset Offset from the session start to the points in the AutomationLines.
5603 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5605 /* find the lines that overlap the ranges being dragged */
5606 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5607 while (i != lines.end ()) {
5608 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5611 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5613 /* check this range against all the AudioRanges that we are using */
5614 list<AudioRange>::const_iterator k = _ranges.begin ();
5615 while (k != _ranges.end()) {
5616 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5622 /* add it to our list if it overlaps at all */
5623 if (k != _ranges.end()) {
5628 _lines.push_back (n);
5634 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5638 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5640 return 1.0 - ((global_y - _y_origin) / line->height());
5644 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5646 const double v = list->eval(x);
5647 return _integral ? rint(v) : v;
5651 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5653 Drag::start_grab (event, cursor);
5655 /* Get line states before we start changing things */
5656 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5657 i->state = &i->line->get_state ();
5658 i->original_fraction = y_fraction (i->line, current_pointer_y());
5661 if (_ranges.empty()) {
5663 /* No selected time ranges: drag all points */
5664 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5665 uint32_t const N = i->line->npoints ();
5666 for (uint32_t j = 0; j < N; ++j) {
5667 i->points.push_back (i->line->nth (j));
5673 if (_nothing_to_drag) {
5679 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5681 if (_nothing_to_drag && !first_move) {
5686 _editor->begin_reversible_command (_("automation range move"));
5688 if (!_ranges.empty()) {
5690 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5692 framecnt_t const half = (i->start + i->end) / 2;
5694 /* find the line that this audio range starts in */
5695 list<Line>::iterator j = _lines.begin();
5696 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5700 if (j != _lines.end()) {
5701 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5703 /* j is the line that this audio range starts in; fade into it;
5704 64 samples length plucked out of thin air.
5707 framepos_t a = i->start + 64;
5712 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5713 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5715 XMLNode &before = the_list->get_state();
5716 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5717 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5719 if (add_p || add_q) {
5720 _editor->session()->add_command (
5721 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5725 /* same thing for the end */
5728 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5732 if (j != _lines.end()) {
5733 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5735 /* j is the line that this audio range starts in; fade out of it;
5736 64 samples length plucked out of thin air.
5739 framepos_t b = i->end - 64;
5744 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5745 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5747 XMLNode &before = the_list->get_state();
5748 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5749 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5751 if (add_p || add_q) {
5752 _editor->session()->add_command (
5753 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5758 _nothing_to_drag = true;
5760 /* Find all the points that should be dragged and put them in the relevant
5761 points lists in the Line structs.
5764 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5766 uint32_t const N = i->line->npoints ();
5767 for (uint32_t j = 0; j < N; ++j) {
5769 /* here's a control point on this line */
5770 ControlPoint* p = i->line->nth (j);
5771 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5773 /* see if it's inside a range */
5774 list<AudioRange>::const_iterator k = _ranges.begin ();
5775 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5779 if (k != _ranges.end()) {
5780 /* dragging this point */
5781 _nothing_to_drag = false;
5782 i->points.push_back (p);
5788 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5789 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5793 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5794 float const f = y_fraction (l->line, current_pointer_y());
5795 /* we are ignoring x position for this drag, so we can just pass in anything */
5796 pair<double, float> result;
5798 result = l->line->drag_motion (0, f, true, false, ignored);
5799 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5804 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5806 if (_nothing_to_drag || !motion_occurred) {
5810 motion (event, false);
5811 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5812 i->line->end_drag (false, 0);
5815 _editor->commit_reversible_command ();
5819 AutomationRangeDrag::aborted (bool)
5821 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5826 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5828 , initial_time_axis_view (itav)
5830 /* note that time_axis_view may be null if the regionview was created
5831 * as part of a copy operation.
5833 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5834 layer = v->region()->layer ();
5835 initial_y = v->get_canvas_group()->position().y;
5836 initial_playlist = v->region()->playlist ();
5837 initial_position = v->region()->position ();
5838 initial_end = v->region()->position () + v->region()->length ();
5841 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5842 : Drag (e, i->canvas_item ())
5845 , _cumulative_dx (0)
5847 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5848 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5853 PatchChangeDrag::motion (GdkEvent* ev, bool)
5855 framepos_t f = adjusted_current_frame (ev);
5856 boost::shared_ptr<Region> r = _region_view->region ();
5857 f = max (f, r->position ());
5858 f = min (f, r->last_frame ());
5860 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5861 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5862 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5863 _cumulative_dx = dxu;
5867 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5869 if (!movement_occurred) {
5873 boost::shared_ptr<Region> r (_region_view->region ());
5874 framepos_t f = adjusted_current_frame (ev);
5875 f = max (f, r->position ());
5876 f = min (f, r->last_frame ());
5878 _region_view->move_patch_change (
5880 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5885 PatchChangeDrag::aborted (bool)
5887 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5891 PatchChangeDrag::setup_pointer_frame_offset ()
5893 boost::shared_ptr<Region> region = _region_view->region ();
5894 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5897 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5898 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5905 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5907 _region_view->update_drag_selection (
5909 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5913 MidiRubberbandSelectDrag::deselect_things ()
5918 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5919 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5922 _vertical_only = true;
5926 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5928 double const y = _region_view->midi_view()->y_position ();
5930 y1 = max (0.0, y1 - y);
5931 y2 = max (0.0, y2 - y);
5933 _region_view->update_vertical_drag_selection (
5936 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5941 MidiVerticalSelectDrag::deselect_things ()
5946 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5947 : RubberbandSelectDrag (e, i)
5953 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5955 if (drag_in_progress) {
5956 /* We just want to select things at the end of the drag, not during it */
5960 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5962 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5964 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5966 _editor->commit_reversible_selection_op ();
5970 EditorRubberbandSelectDrag::deselect_things ()
5972 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5974 _editor->selection->clear_tracks();
5975 _editor->selection->clear_regions();
5976 _editor->selection->clear_points ();
5977 _editor->selection->clear_lines ();
5978 _editor->selection->clear_midi_notes ();
5980 _editor->commit_reversible_selection_op();
5983 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5988 _note[0] = _note[1] = 0;
5991 NoteCreateDrag::~NoteCreateDrag ()
5997 NoteCreateDrag::grid_frames (framepos_t t) const
6000 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6002 grid_beats = Evoral::Beats(1);
6005 return _region_view->region_beats_to_region_frames (grid_beats);
6009 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6011 Drag::start_grab (event, cursor);
6013 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6015 framepos_t pf = _drags->current_pointer_frame ();
6016 framecnt_t const g = grid_frames (pf);
6018 /* Hack so that we always snap to the note that we are over, instead of snapping
6019 to the next one if we're more than halfway through the one we're over.
6021 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6025 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6026 _note[1] = _note[0];
6028 MidiStreamView* sv = _region_view->midi_stream_view ();
6029 double const x = _editor->sample_to_pixel (_note[0]);
6030 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6032 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6033 _drag_rect->set_outline_all ();
6034 _drag_rect->set_outline_color (0xffffff99);
6035 _drag_rect->set_fill_color (0xffffff66);
6039 NoteCreateDrag::motion (GdkEvent* event, bool)
6041 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6042 double const x0 = _editor->sample_to_pixel (_note[0]);
6043 double const x1 = _editor->sample_to_pixel (_note[1]);
6044 _drag_rect->set_x0 (std::min(x0, x1));
6045 _drag_rect->set_x1 (std::max(x0, x1));
6049 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6051 if (!had_movement) {
6055 framepos_t const start = min (_note[0], _note[1]);
6056 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6058 framecnt_t const g = grid_frames (start);
6059 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6061 if (_editor->snap_mode() == SnapNormal && length < g) {
6065 Evoral::Beats length_beats = max (
6066 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6068 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6072 NoteCreateDrag::y_to_region (double y) const
6075 _region_view->get_canvas_group()->canvas_to_item (x, y);
6080 NoteCreateDrag::aborted (bool)
6085 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6090 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6094 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6096 Drag::start_grab (event, cursor);
6100 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6106 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6109 distance = _drags->current_pointer_x() - grab_x();
6110 len = ar->fade_in()->back()->when;
6112 distance = grab_x() - _drags->current_pointer_x();
6113 len = ar->fade_out()->back()->when;
6116 /* how long should it be ? */
6118 new_length = len + _editor->pixel_to_sample (distance);
6120 /* now check with the region that this is legal */
6122 new_length = ar->verify_xfade_bounds (new_length, start);
6125 arv->reset_fade_in_shape_width (ar, new_length);
6127 arv->reset_fade_out_shape_width (ar, new_length);
6132 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6138 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6141 distance = _drags->current_pointer_x() - grab_x();
6142 len = ar->fade_in()->back()->when;
6144 distance = grab_x() - _drags->current_pointer_x();
6145 len = ar->fade_out()->back()->when;
6148 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6150 _editor->begin_reversible_command ("xfade trim");
6151 ar->playlist()->clear_owned_changes ();
6154 ar->set_fade_in_length (new_length);
6156 ar->set_fade_out_length (new_length);
6159 /* Adjusting the xfade may affect other regions in the playlist, so we need
6160 to get undo Commands from the whole playlist rather than just the
6164 vector<Command*> cmds;
6165 ar->playlist()->rdiff (cmds);
6166 _editor->session()->add_commands (cmds);
6167 _editor->commit_reversible_command ();
6172 CrossfadeEdgeDrag::aborted (bool)
6175 // arv->redraw_start_xfade ();
6177 // arv->redraw_end_xfade ();
6181 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6182 : Drag (e, item, true)
6183 , line (new EditorCursor (*e))
6185 line->set_position (pos);
6189 RegionCutDrag::~RegionCutDrag ()
6195 RegionCutDrag::motion (GdkEvent*, bool)
6197 framepos_t where = _drags->current_pointer_frame();
6198 _editor->snap_to (where);
6200 line->set_position (where);
6204 RegionCutDrag::finished (GdkEvent*, bool)
6206 _editor->get_track_canvas()->canvas()->re_enter();
6208 framepos_t pos = _drags->current_pointer_frame();
6212 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6218 _editor->split_regions_at (pos, rs);
6222 RegionCutDrag::aborted (bool)