2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "ui_config.h"
68 #include "verbose_cursor.h"
71 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
82 DragManager::DragManager (Editor* e)
85 , _current_pointer_x (0.0)
86 , _current_pointer_y (0.0)
87 , _current_pointer_frame (0)
88 , _old_follow_playhead (false)
92 DragManager::~DragManager ()
97 /** Call abort for each active drag */
103 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 if (!_drags.empty ()) {
109 _editor->set_follow_playhead (_old_follow_playhead, false);
113 _editor->abort_reversible_command();
119 DragManager::add (Drag* d)
121 d->set_manager (this);
122 _drags.push_back (d);
126 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
128 d->set_manager (this);
129 _drags.push_back (d);
134 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
136 /* Prevent follow playhead during the drag to be nice to the user */
137 _old_follow_playhead = _editor->follow_playhead ();
138 _editor->set_follow_playhead (false);
140 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
142 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
143 (*i)->start_grab (e, c);
147 /** Call end_grab for each active drag.
148 * @return true if any drag reported movement having occurred.
151 DragManager::end_grab (GdkEvent* e)
156 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
157 bool const t = (*i)->end_grab (e);
168 _editor->set_follow_playhead (_old_follow_playhead, false);
174 DragManager::mark_double_click ()
176 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
177 (*i)->set_double_click (true);
182 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
186 /* calling this implies that we expect the event to have canvas
189 * Can we guarantee that this is true?
192 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
194 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
195 bool const t = (*i)->motion_handler (e, from_autoscroll);
196 /* run all handlers; return true if at least one of them
197 returns true (indicating that the event has been handled).
209 DragManager::have_item (ArdourCanvas::Item* i) const
211 list<Drag*>::const_iterator j = _drags.begin ();
212 while (j != _drags.end() && (*j)->item () != i) {
216 return j != _drags.end ();
219 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
223 , _pointer_frame_offset (0)
224 , _x_constrained (false)
225 , _y_constrained (false)
226 , _was_rolling (false)
227 , _trackview_only (trackview_only)
228 , _move_threshold_passed (false)
229 , _starting_point_passed (false)
230 , _initially_vertical (false)
231 , _was_double_click (false)
234 , _last_pointer_x (0.0)
235 , _last_pointer_y (0.0)
236 , _raw_grab_frame (0)
238 , _last_pointer_frame (0)
245 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
251 _cursor_ctx = CursorContext::create (*_editor, cursor);
253 _cursor_ctx->change (cursor);
260 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
263 /* we set up x/y dragging constraints on first move */
265 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::snap_delta (guint state) const
349 if (ArdourKeyboard::indicates_snap_delta (state)) {
357 Drag::current_pointer_x() const
359 return _drags->current_pointer_x ();
363 Drag::current_pointer_y () const
365 if (!_trackview_only) {
366 return _drags->current_pointer_y ();
369 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
373 Drag::setup_snap_delta (framepos_t pos)
375 framepos_t temp = pos;
376 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
377 _snap_delta = temp - pos;
381 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
383 /* check to see if we have moved in any way that matters since the last motion event */
384 if (_move_threshold_passed &&
385 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
386 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
390 pair<framecnt_t, int> const threshold = move_threshold ();
392 bool const old_move_threshold_passed = _move_threshold_passed;
394 if (!_move_threshold_passed) {
396 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
397 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
399 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
402 if (active (_editor->mouse_mode) && _move_threshold_passed) {
404 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
406 if (old_move_threshold_passed != _move_threshold_passed) {
410 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
411 _initially_vertical = true;
413 _initially_vertical = false;
415 /** check constraints for this drag.
416 * Note that the current convention is to use "contains" for
417 * key modifiers during motion and "equals" when initiating a drag.
418 * In this case we haven't moved yet, so "equals" applies here.
420 if (Config->get_edit_mode() != Lock) {
421 if (event->motion.state & Gdk::BUTTON2_MASK) {
422 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
423 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
424 _x_constrained = false;
425 _y_constrained = true;
427 _x_constrained = true;
428 _y_constrained = false;
430 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
431 // if dragging normally, the motion is constrained to the first direction of movement.
432 if (_initially_vertical) {
433 _x_constrained = true;
434 _y_constrained = false;
436 _x_constrained = false;
437 _y_constrained = true;
441 if (event->button.state & Gdk::BUTTON2_MASK) {
442 _x_constrained = false;
444 _x_constrained = true;
446 _y_constrained = false;
450 if (!from_autoscroll) {
451 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
454 if (!_editor->autoscroll_active() || from_autoscroll) {
457 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
459 motion (event, first_move && !_starting_point_passed);
461 if (first_move && !_starting_point_passed) {
462 _starting_point_passed = true;
465 _last_pointer_x = _drags->current_pointer_x ();
466 _last_pointer_y = current_pointer_y ();
467 _last_pointer_frame = adjusted_current_frame (event, false);
477 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
485 aborted (_move_threshold_passed);
487 _editor->stop_canvas_autoscroll ();
488 _editor->verbose_cursor()->hide ();
492 Drag::show_verbose_cursor_time (framepos_t frame)
494 _editor->verbose_cursor()->set_time (frame);
495 _editor->verbose_cursor()->show ();
499 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
501 _editor->verbose_cursor()->set_duration (start, end);
502 _editor->verbose_cursor()->show ();
506 Drag::show_verbose_cursor_text (string const & text)
508 _editor->verbose_cursor()->set (text);
509 _editor->verbose_cursor()->show ();
512 boost::shared_ptr<Region>
513 Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
515 if (_editor->session()) {
516 const TempoMap& map (_editor->session()->tempo_map());
517 framecnt_t pos = grab_frame();
518 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, commit);
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 (), event, false);
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 framecnt_t const sd = snap_delta (event->button.state);
661 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
663 _editor->snap_to_with_modifier (sync_frame, event);
665 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
668 *pending_region_position = _last_frame_position;
671 if (*pending_region_position > max_framepos - _primary->region()->length()) {
672 *pending_region_position = _last_frame_position;
677 bool const x_move_allowed = !_x_constrained;
679 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
681 /* x movement since last time (in pixels) */
682 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
684 /* total x movement */
685 framecnt_t total_dx = *pending_region_position;
686 if (regions_came_from_canvas()) {
687 total_dx = total_dx - grab_frame ();
690 /* check that no regions have gone off the start of the session */
691 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
692 if ((i->view->region()->position() + total_dx) < 0) {
694 *pending_region_position = _last_frame_position;
705 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
711 const int tavsize = _time_axis_views.size();
712 const int dt = delta > 0 ? +1 : -1;
714 int target = start + delta - skip;
716 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
717 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
719 while (current >= 0 && current != target) {
721 if (current < 0 && dt < 0) {
724 if (current >= tavsize && dt > 0) {
727 if (current < 0 || current >= tavsize) {
731 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
732 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
736 if (distance_only && current == start + delta) {
744 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
746 if (_y_constrained) {
750 const int tavsize = _time_axis_views.size();
751 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
752 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
753 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
755 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
756 /* already in the drop zone */
757 if (delta_track >= 0) {
758 /* downward motion - OK if others are still not in the dropzone */
767 } else if (n >= tavsize) {
768 /* downward motion into drop zone. That's fine. */
772 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
773 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
774 /* not a track, or the wrong type */
778 double const l = i->layer + delta_layer;
780 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
781 mode to allow the user to place a region below another on layer 0.
783 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
784 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
785 If it has, the layers will be munged later anyway, so it's ok.
791 /* all regions being dragged are ok with this change */
795 struct DraggingViewSorter {
796 bool operator() (const DraggingView& a, const DraggingView& b) {
797 return a.time_axis_view < b.time_axis_view;
802 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
804 double delta_layer = 0;
805 int delta_time_axis_view = 0;
806 int current_pointer_time_axis_view = -1;
808 assert (!_views.empty ());
810 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
812 /* Find the TimeAxisView that the pointer is now over */
813 const double cur_y = current_pointer_y ();
814 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
815 TimeAxisView* tv = r.first;
817 if (!tv && cur_y < 0) {
818 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
822 /* find drop-zone y-position */
823 Coord last_track_bottom_edge;
824 last_track_bottom_edge = 0;
825 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
826 if (!(*t)->hidden()) {
827 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
832 if (tv && tv->view()) {
833 /* the mouse is over a track */
834 double layer = r.second;
836 if (first_move && tv->view()->layer_display() == Stacked) {
837 tv->view()->set_layer_display (Expanded);
840 /* Here's the current pointer position in terms of time axis view and layer */
841 current_pointer_time_axis_view = find_time_axis_view (tv);
842 assert(current_pointer_time_axis_view >= 0);
844 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
846 /* Work out the change in y */
848 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
849 if (!rtv || !rtv->is_track()) {
850 /* ignore busses early on. we can't move any regions on them */
851 } else if (_last_pointer_time_axis_view < 0) {
852 /* Was in the drop-zone, now over a track.
853 * Hence it must be an upward move (from the bottom)
855 * track_index is still -1, so delta must be set to
856 * move up the correct number of tracks from the bottom.
858 * This is necessary because steps may be skipped if
859 * the bottom-most track is not a valid target and/or
860 * if there are hidden tracks at the bottom.
861 * Hence the initial offset (_ddropzone) as well as the
862 * last valid pointer position (_pdropzone) need to be
863 * taken into account.
865 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
867 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
870 /* TODO needs adjustment per DraggingView,
872 * e.g. select one region on the top-layer of a track
873 * and one region which is at the bottom-layer of another track
876 * Indicated drop-zones and layering is wrong.
877 * and may infer additional layers on the target-track
878 * (depending how many layers the original track had).
880 * Or select two regions (different layers) on a same track,
881 * move across a non-layer track.. -> layering info is lost.
882 * on drop either of the regions may be on top.
884 * Proposed solution: screw it :) well,
885 * don't use delta_layer, use an absolute value
886 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
887 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
888 * 3) iterate over all DraggingView, find the one that is over the track with most layers
889 * 4) proportionally scale layer to layers available on target
891 delta_layer = current_pointer_layer - _last_pointer_layer;
894 /* for automation lanes, there is a TimeAxisView but no ->view()
895 * if (!tv) -> dropzone
897 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
898 /* Moving into the drop-zone.. */
899 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
900 /* delta_time_axis_view may not be sufficient to move into the DZ
901 * the mouse may enter it, but it may not be a valid move due to
904 * -> remember the delta needed to move into the dropzone
906 _ddropzone = delta_time_axis_view;
907 /* ..but subtract hidden tracks (or routes) at the bottom.
908 * we silently move mover them
910 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
911 - _time_axis_views.size();
913 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
914 /* move around inside the zone.
915 * This allows to move further down until all regions are in the zone.
917 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
918 assert(ptr_y >= last_track_bottom_edge);
919 assert(_ddropzone > 0);
921 /* calculate mouse position in 'tracks' below last track. */
922 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
923 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
925 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
927 delta_time_axis_view = dzpos - _pdropzone;
928 } else if (dzpos < _pdropzone && _ndropzone > 0) {
929 // move up inside the DZ
930 delta_time_axis_view = dzpos - _pdropzone;
934 /* Work out the change in x */
935 framepos_t pending_region_position;
936 double const x_delta = compute_x_delta (event, &pending_region_position);
937 _last_frame_position = pending_region_position;
939 /* calculate hidden tracks in current y-axis delta */
941 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
942 /* The mouse is more than one track below the dropzone.
943 * distance calculation is not needed (and would not work, either
944 * because the dropzone is "packed").
946 * Except when [partially] moving regions out of dropzone in a large step.
947 * (the mouse may or may not remain in the DZ)
948 * Hidden tracks at the bottom of the TAV need to be skipped.
950 * This also handles the case if the mouse entered the DZ
951 * in a large step (exessive delta), either due to fast-movement,
952 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
954 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
955 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
957 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
958 -_time_axis_views.size() - dt;
961 else if (_last_pointer_time_axis_view < 0) {
962 /* Moving out of the zone. Check for hidden tracks at the bottom. */
963 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
964 -_time_axis_views.size() - delta_time_axis_view;
966 /* calculate hidden tracks that are skipped by the pointer movement */
967 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
968 - _last_pointer_time_axis_view
969 - delta_time_axis_view;
972 /* Verify change in y */
973 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
974 /* this y movement is not allowed, so do no y movement this time */
975 delta_time_axis_view = 0;
980 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
981 /* haven't reached next snap point, and we're not switching
982 trackviews nor layers. nothing to do.
987 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
988 PlaylistDropzoneMap playlist_dropzone_map;
989 _ndropzone = 0; // number of elements currently in the dropzone
992 /* sort views by time_axis.
993 * This retains track order in the dropzone, regardless
994 * of actual selection order
996 _views.sort (DraggingViewSorter());
998 /* count number of distinct tracks of all regions
999 * being dragged, used for dropzone.
1001 int prev_track = -1;
1002 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1003 if (i->time_axis_view != prev_track) {
1004 prev_track = i->time_axis_view;
1010 _views.back().time_axis_view -
1011 _views.front().time_axis_view;
1013 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1014 - _views.back().time_axis_view;
1016 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1020 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1022 RegionView* rv = i->view;
1027 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1034 /* reparent the regionview into a group above all
1038 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1039 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1040 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1041 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1042 /* move the item so that it continues to appear at the
1043 same location now that its parent has changed.
1045 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1048 /* If we have moved tracks, we'll fudge the layer delta so that the
1049 region gets moved back onto layer 0 on its new track; this avoids
1050 confusion when dragging regions from non-zero layers onto different
1053 double this_delta_layer = delta_layer;
1054 if (delta_time_axis_view != 0) {
1055 this_delta_layer = - i->layer;
1058 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1060 int track_index = i->time_axis_view + this_delta_time_axis_view;
1061 assert(track_index >= 0);
1063 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1064 /* Track is in the Dropzone */
1066 i->time_axis_view = track_index;
1067 assert(i->time_axis_view >= (int) _time_axis_views.size());
1070 double yposition = 0;
1071 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1072 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1075 /* store index of each new playlist as a negative count, starting at -1 */
1077 if (pdz == playlist_dropzone_map.end()) {
1078 /* compute where this new track (which doesn't exist yet) will live
1081 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1083 /* How high is this region view ? */
1085 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1086 ArdourCanvas::Rect bbox;
1089 bbox = obbox.get ();
1092 last_track_bottom_edge += bbox.height();
1094 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1097 yposition = pdz->second;
1100 /* values are zero or negative, hence the use of min() */
1101 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1106 /* The TimeAxisView that this region is now over */
1107 TimeAxisView* current_tv = _time_axis_views[track_index];
1109 /* Ensure it is moved from stacked -> expanded if appropriate */
1110 if (current_tv->view()->layer_display() == Stacked) {
1111 current_tv->view()->set_layer_display (Expanded);
1114 /* We're only allowed to go -ve in layer on Expanded views */
1115 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1116 this_delta_layer = - i->layer;
1120 rv->set_height (current_tv->view()->child_height ());
1122 /* Update show/hidden status as the region view may have come from a hidden track,
1123 or have moved to one.
1125 if (current_tv->hidden ()) {
1126 rv->get_canvas_group()->hide ();
1128 rv->get_canvas_group()->show ();
1131 /* Update the DraggingView */
1132 i->time_axis_view = track_index;
1133 i->layer += this_delta_layer;
1136 _editor->mouse_brush_insert_region (rv, pending_region_position);
1140 /* Get the y coordinate of the top of the track that this region is now over */
1141 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1143 /* And adjust for the layer that it should be on */
1144 StreamView* cv = current_tv->view ();
1145 switch (cv->layer_display ()) {
1149 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1152 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1156 /* need to get the parent of the regionview
1157 * canvas group and get its position in
1158 * equivalent coordinate space as the trackview
1159 * we are now dragging over.
1162 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1167 /* Now move the region view */
1168 rv->move (x_delta, y_delta);
1170 } /* foreach region */
1172 _total_x_delta += x_delta;
1174 if (x_delta != 0 && !_brushing) {
1175 show_verbose_cursor_time (_last_frame_position);
1178 /* keep track of pointer movement */
1180 /* the pointer is currently over a time axis view */
1182 if (_last_pointer_time_axis_view < 0) {
1183 /* last motion event was not over a time axis view
1184 * or last y-movement out of the dropzone was not valid
1187 if (delta_time_axis_view < 0) {
1188 /* in the drop zone, moving up */
1190 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1191 * We do not use negative _last_pointer_time_axis_view because
1192 * the dropzone is "packed" (the actual track offset is ignored)
1194 * As opposed to the actual number
1195 * of elements in the dropzone (_ndropzone)
1196 * _pdropzone is not constrained. This is necessary
1197 * to allow moving multiple regions with y-distance
1200 * There can be 0 elements in the dropzone,
1201 * even though the drag-pointer is inside the DZ.
1204 * [ Audio-track, Midi-track, Audio-track, DZ ]
1205 * move regions from both audio tracks at the same time into the
1206 * DZ by grabbing the region in the bottom track.
1208 assert(current_pointer_time_axis_view >= 0);
1209 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1213 /* only move out of the zone if the movement is OK */
1214 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1215 assert(delta_time_axis_view < 0);
1216 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1217 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1218 * the current position can be calculated as follows:
1220 // a well placed oofus attack can still throw this off.
1221 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1222 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1225 /* last motion event was also over a time axis view */
1226 _last_pointer_time_axis_view += delta_time_axis_view;
1227 assert(_last_pointer_time_axis_view >= 0);
1232 /* the pointer is not over a time axis view */
1233 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1234 _pdropzone += delta_time_axis_view - delta_skip;
1235 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1238 _last_pointer_layer += delta_layer;
1242 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1244 if (_copy && first_move) {
1245 if (_x_constrained && !_brushing) {
1246 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1247 } else if (!_brushing) {
1248 _editor->begin_reversible_command (Operations::region_copy);
1249 } else if (_brushing) {
1250 _editor->begin_reversible_command (Operations::drag_region_brush);
1252 /* duplicate the regionview(s) and region(s) */
1254 list<DraggingView> new_regionviews;
1256 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1258 RegionView* rv = i->view;
1259 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1260 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1262 const boost::shared_ptr<const Region> original = rv->region();
1263 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1264 region_copy->set_position (original->position());
1265 /* need to set this so that the drop zone code can work. This doesn't
1266 actually put the region into the playlist, but just sets a weak pointer
1269 region_copy->set_playlist (original->playlist());
1273 boost::shared_ptr<AudioRegion> audioregion_copy
1274 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1276 nrv = new AudioRegionView (*arv, audioregion_copy);
1278 boost::shared_ptr<MidiRegion> midiregion_copy
1279 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1280 nrv = new MidiRegionView (*mrv, midiregion_copy);
1285 nrv->get_canvas_group()->show ();
1286 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1288 /* swap _primary to the copy */
1290 if (rv == _primary) {
1294 /* ..and deselect the one we copied */
1296 rv->set_selected (false);
1299 if (!new_regionviews.empty()) {
1301 /* reflect the fact that we are dragging the copies */
1303 _views = new_regionviews;
1305 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1308 } else if (!_copy && first_move) {
1309 if (_x_constrained && !_brushing) {
1310 _editor->begin_reversible_command (_("fixed time region drag"));
1311 } else if (!_brushing) {
1312 _editor->begin_reversible_command (Operations::region_drag);
1313 } else if (_brushing) {
1314 _editor->begin_reversible_command (Operations::drag_region_brush);
1317 RegionMotionDrag::motion (event, first_move);
1321 RegionMotionDrag::finished (GdkEvent *, bool)
1323 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1324 if (!(*i)->view()) {
1328 if ((*i)->view()->layer_display() == Expanded) {
1329 (*i)->view()->set_layer_display (Stacked);
1335 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1337 RegionMotionDrag::finished (ev, movement_occurred);
1339 if (!movement_occurred) {
1343 if (was_double_click() && !_views.empty()) {
1344 DraggingView dv = _views.front();
1345 dv.view->show_region_editor ();
1352 assert (!_views.empty ());
1354 /* We might have hidden region views so that they weren't visible during the drag
1355 (when they have been reparented). Now everything can be shown again, as region
1356 views are back in their track parent groups.
1358 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1359 i->view->get_canvas_group()->show ();
1362 bool const changed_position = (_last_frame_position != _primary->region()->position());
1363 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1364 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1384 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1388 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1390 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1395 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1396 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1397 uint32_t output_chan = region->n_channels();
1398 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1399 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1401 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1402 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1404 rtav->set_height (original->current_height());
1408 ChanCount one_midi_port (DataType::MIDI, 1);
1409 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1410 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1411 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1413 rtav->set_height (original->current_height());
1418 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1424 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1426 RegionSelection new_views;
1427 PlaylistSet modified_playlists;
1428 RouteTimeAxisView* new_time_axis_view = 0;
1431 /* all changes were made during motion event handlers */
1433 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1437 _editor->commit_reversible_command ();
1441 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1442 PlaylistMapping playlist_mapping;
1444 /* insert the regions into their new playlists */
1445 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1447 RouteTimeAxisView* dest_rtv = 0;
1449 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1455 if (changed_position && !_x_constrained) {
1456 where = i->view->region()->position() - drag_delta;
1458 where = i->view->region()->position();
1461 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1462 /* dragged to drop zone */
1464 PlaylistMapping::iterator pm;
1466 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1467 /* first region from this original playlist: create a new track */
1468 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1469 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1470 dest_rtv = new_time_axis_view;
1472 /* we already created a new track for regions from this playlist, use it */
1473 dest_rtv = pm->second;
1476 /* destination time axis view is the one we dragged to */
1477 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1480 if (dest_rtv != 0) {
1481 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1482 if (new_view != 0) {
1483 new_views.push_back (new_view);
1487 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1488 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1491 list<DraggingView>::const_iterator next = i;
1497 /* If we've created new regions either by copying or moving
1498 to a new track, we want to replace the old selection with the new ones
1501 if (new_views.size() > 0) {
1502 _editor->selection->set (new_views);
1505 /* write commands for the accumulated diffs for all our modified playlists */
1506 add_stateful_diff_commands_for_playlists (modified_playlists);
1508 _editor->commit_reversible_command ();
1512 RegionMoveDrag::finished_no_copy (
1513 bool const changed_position,
1514 bool const changed_tracks,
1515 framecnt_t const drag_delta
1518 RegionSelection new_views;
1519 PlaylistSet modified_playlists;
1520 PlaylistSet frozen_playlists;
1521 set<RouteTimeAxisView*> views_to_update;
1522 RouteTimeAxisView* new_time_axis_view = 0;
1524 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1525 PlaylistMapping playlist_mapping;
1527 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1529 RegionView* rv = i->view;
1530 RouteTimeAxisView* dest_rtv = 0;
1532 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1537 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1538 /* dragged to drop zone */
1540 PlaylistMapping::iterator pm;
1542 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1543 /* first region from this original playlist: create a new track */
1544 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1545 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1546 dest_rtv = new_time_axis_view;
1548 /* we already created a new track for regions from this playlist, use it */
1549 dest_rtv = pm->second;
1553 /* destination time axis view is the one we dragged to */
1554 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1559 double const dest_layer = i->layer;
1561 views_to_update.insert (dest_rtv);
1565 if (changed_position && !_x_constrained) {
1566 where = rv->region()->position() - drag_delta;
1568 where = rv->region()->position();
1571 if (changed_tracks) {
1573 /* insert into new playlist */
1575 RegionView* new_view = insert_region_into_playlist (
1576 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1579 if (new_view == 0) {
1584 new_views.push_back (new_view);
1586 /* remove from old playlist */
1588 /* the region that used to be in the old playlist is not
1589 moved to the new one - we use a copy of it. as a result,
1590 any existing editor for the region should no longer be
1593 rv->hide_region_editor();
1596 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1600 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1602 /* this movement may result in a crossfade being modified, or a layering change,
1603 so we need to get undo data from the playlist as well as the region.
1606 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1608 playlist->clear_changes ();
1611 rv->region()->clear_changes ();
1614 motion on the same track. plonk the previously reparented region
1615 back to its original canvas group (its streamview).
1616 No need to do anything for copies as they are fake regions which will be deleted.
1619 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1620 rv->get_canvas_group()->set_y_position (i->initial_y);
1623 /* just change the model */
1624 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1625 playlist->set_layer (rv->region(), dest_layer);
1628 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1630 r = frozen_playlists.insert (playlist);
1633 playlist->freeze ();
1636 rv->region()->set_position (where);
1637 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1640 if (changed_tracks) {
1642 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1643 was selected in all of them, then removing it from a playlist will have removed all
1644 trace of it from _views (i.e. there were N regions selected, we removed 1,
1645 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1646 corresponding regionview, and _views is now empty).
1648 This could have invalidated any and all iterators into _views.
1650 The heuristic we use here is: if the region selection is empty, break out of the loop
1651 here. if the region selection is not empty, then restart the loop because we know that
1652 we must have removed at least the region(view) we've just been working on as well as any
1653 that we processed on previous iterations.
1655 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1656 we can just iterate.
1660 if (_views.empty()) {
1671 /* If we've created new regions either by copying or moving
1672 to a new track, we want to replace the old selection with the new ones
1675 if (new_views.size() > 0) {
1676 _editor->selection->set (new_views);
1679 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1683 /* write commands for the accumulated diffs for all our modified playlists */
1684 add_stateful_diff_commands_for_playlists (modified_playlists);
1685 /* applies to _brushing */
1686 _editor->commit_reversible_command ();
1688 /* We have futzed with the layering of canvas items on our streamviews.
1689 If any region changed layer, this will have resulted in the stream
1690 views being asked to set up their region views, and all will be well.
1691 If not, we might now have badly-ordered region views. Ask the StreamViews
1692 involved to sort themselves out, just in case.
1695 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1696 (*i)->view()->playlist_layered ((*i)->track ());
1700 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1701 * @param region Region to remove.
1702 * @param playlist playlist To remove from.
1703 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1704 * that clear_changes () is only called once per playlist.
1707 RegionMoveDrag::remove_region_from_playlist (
1708 boost::shared_ptr<Region> region,
1709 boost::shared_ptr<Playlist> playlist,
1710 PlaylistSet& modified_playlists
1713 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1716 playlist->clear_changes ();
1719 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1723 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1724 * clearing the playlist's diff history first if necessary.
1725 * @param region Region to insert.
1726 * @param dest_rtv Destination RouteTimeAxisView.
1727 * @param dest_layer Destination layer.
1728 * @param where Destination position.
1729 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1730 * that clear_changes () is only called once per playlist.
1731 * @return New RegionView, or 0 if no insert was performed.
1734 RegionMoveDrag::insert_region_into_playlist (
1735 boost::shared_ptr<Region> region,
1736 RouteTimeAxisView* dest_rtv,
1739 PlaylistSet& modified_playlists
1742 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1743 if (!dest_playlist) {
1747 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1748 _new_region_view = 0;
1749 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1751 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1752 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1754 dest_playlist->clear_changes ();
1757 dest_playlist->add_region (region, where);
1759 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1760 dest_playlist->set_layer (region, dest_layer);
1765 assert (_new_region_view);
1767 return _new_region_view;
1771 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1773 _new_region_view = rv;
1777 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1779 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1780 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1782 _editor->session()->add_command (c);
1791 RegionMoveDrag::aborted (bool movement_occurred)
1795 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1796 list<DraggingView>::const_iterator next = i;
1805 RegionMotionDrag::aborted (movement_occurred);
1810 RegionMotionDrag::aborted (bool)
1812 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1814 StreamView* sview = (*i)->view();
1817 if (sview->layer_display() == Expanded) {
1818 sview->set_layer_display (Stacked);
1823 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1824 RegionView* rv = i->view;
1825 TimeAxisView* tv = &(rv->get_time_axis_view ());
1826 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1828 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1829 rv->get_canvas_group()->set_y_position (0);
1831 rv->move (-_total_x_delta, 0);
1832 rv->set_height (rtv->view()->child_height ());
1836 /** @param b true to brush, otherwise false.
1837 * @param c true to make copies of the regions being moved, otherwise false.
1839 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1840 : RegionMotionDrag (e, i, p, v, b)
1842 , _new_region_view (0)
1844 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1847 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1848 if (rtv && rtv->is_track()) {
1849 speed = rtv->track()->speed ();
1852 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1856 RegionMoveDrag::setup_pointer_frame_offset ()
1858 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1861 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1862 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1864 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1866 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1867 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1869 _primary = v->view()->create_region_view (r, false, false);
1871 _primary->get_canvas_group()->show ();
1872 _primary->set_position (pos, 0);
1873 _views.push_back (DraggingView (_primary, this, v));
1875 _last_frame_position = pos;
1877 _item = _primary->get_canvas_group ();
1881 RegionInsertDrag::finished (GdkEvent *, bool)
1883 int pos = _views.front().time_axis_view;
1884 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1886 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1888 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1889 _primary->get_canvas_group()->set_y_position (0);
1891 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1893 _editor->begin_reversible_command (Operations::insert_region);
1894 playlist->clear_changes ();
1895 playlist->add_region (_primary->region (), _last_frame_position);
1897 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1898 if (Config->get_edit_mode() == Ripple) {
1899 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1902 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1903 _editor->commit_reversible_command ();
1911 RegionInsertDrag::aborted (bool)
1918 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1919 : RegionMoveDrag (e, i, p, v, false, false)
1921 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1924 struct RegionSelectionByPosition {
1925 bool operator() (RegionView*a, RegionView* b) {
1926 return a->region()->position () < b->region()->position();
1931 RegionSpliceDrag::motion (GdkEvent* event, bool)
1933 /* Which trackview is this ? */
1935 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1936 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1938 /* The region motion is only processed if the pointer is over
1942 if (!tv || !tv->is_track()) {
1943 /* To make sure we hide the verbose canvas cursor when the mouse is
1944 not held over an audio track.
1946 _editor->verbose_cursor()->hide ();
1949 _editor->verbose_cursor()->show ();
1954 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1960 RegionSelection copy;
1961 _editor->selection->regions.by_position(copy);
1963 framepos_t const pf = adjusted_current_frame (event);
1965 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1967 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1973 boost::shared_ptr<Playlist> playlist;
1975 if ((playlist = atv->playlist()) == 0) {
1979 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1984 if (pf < (*i)->region()->last_frame() + 1) {
1988 if (pf > (*i)->region()->first_frame()) {
1994 playlist->shuffle ((*i)->region(), dir);
1999 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2001 RegionMoveDrag::finished (event, movement_occurred);
2005 RegionSpliceDrag::aborted (bool)
2015 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2018 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2020 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2021 RegionSelection to_ripple;
2022 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2023 if ((*i)->position() >= where) {
2024 to_ripple.push_back (rtv->view()->find_view(*i));
2028 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2029 if (!exclude.contains (*i)) {
2030 // the selection has already been added to _views
2032 if (drag_in_progress) {
2033 // do the same things that RegionMotionDrag::motion does when
2034 // first_move is true, for the region views that we're adding
2035 // to _views this time
2038 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2039 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2040 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2041 rvg->reparent (_editor->_drag_motion_group);
2043 // we only need to move in the y direction
2044 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2049 _views.push_back (DraggingView (*i, this, tav));
2055 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2058 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2059 // we added all the regions after the selection
2061 std::list<DraggingView>::iterator to_erase = i++;
2062 if (!_editor->selection->regions.contains (to_erase->view)) {
2063 // restore the non-selected regions to their original playlist & positions,
2064 // and then ripple them back by the length of the regions that were dragged away
2065 // do the same things as RegionMotionDrag::aborted
2067 RegionView *rv = to_erase->view;
2068 TimeAxisView* tv = &(rv->get_time_axis_view ());
2069 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2072 // plonk them back onto their own track
2073 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2074 rv->get_canvas_group()->set_y_position (0);
2078 // move the underlying region to match the view
2079 rv->region()->set_position (rv->region()->position() + amount);
2081 // restore the view to match the underlying region's original position
2082 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2085 rv->set_height (rtv->view()->child_height ());
2086 _views.erase (to_erase);
2092 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2094 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2096 return allow_moves_across_tracks;
2104 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2105 : RegionMoveDrag (e, i, p, v, false, false)
2107 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2108 // compute length of selection
2109 RegionSelection selected_regions = _editor->selection->regions;
2110 selection_length = selected_regions.end_frame() - selected_regions.start();
2112 // we'll only allow dragging to another track in ripple mode if all the regions
2113 // being dragged start off on the same track
2114 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2117 exclude = new RegionList;
2118 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2119 exclude->push_back((*i)->region());
2122 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2123 RegionSelection copy;
2124 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2126 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2127 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2129 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2130 // find ripple start point on each applicable playlist
2131 RegionView *first_selected_on_this_track = NULL;
2132 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2133 if ((*i)->region()->playlist() == (*pi)) {
2134 // region is on this playlist - it's the first, because they're sorted
2135 first_selected_on_this_track = *i;
2139 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2140 add_all_after_to_views (
2141 &first_selected_on_this_track->get_time_axis_view(),
2142 first_selected_on_this_track->region()->position(),
2143 selected_regions, false);
2146 if (allow_moves_across_tracks) {
2147 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2155 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2157 /* Which trackview is this ? */
2159 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2160 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2162 /* The region motion is only processed if the pointer is over
2166 if (!tv || !tv->is_track()) {
2167 /* To make sure we hide the verbose canvas cursor when the mouse is
2168 not held over an audiotrack.
2170 _editor->verbose_cursor()->hide ();
2174 framepos_t where = adjusted_current_frame (event);
2175 assert (where >= 0);
2177 double delta = compute_x_delta (event, &after);
2179 framecnt_t amount = _editor->pixel_to_sample (delta);
2181 if (allow_moves_across_tracks) {
2182 // all the originally selected regions were on the same track
2184 framecnt_t adjust = 0;
2185 if (prev_tav && tv != prev_tav) {
2186 // dragged onto a different track
2187 // remove the unselected regions from _views, restore them to their original positions
2188 // and add the regions after the drop point on the new playlist to _views instead.
2189 // undo the effect of rippling the previous playlist, and include the effect of removing
2190 // the dragged region(s) from this track
2192 remove_unselected_from_views (prev_amount, false);
2193 // ripple previous playlist according to the regions that have been removed onto the new playlist
2194 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2197 // move just the selected regions
2198 RegionMoveDrag::motion(event, first_move);
2200 // ensure that the ripple operation on the new playlist inserts selection_length time
2201 adjust = selection_length;
2202 // ripple the new current playlist
2203 tv->playlist()->ripple (where, amount+adjust, exclude);
2205 // add regions after point where drag entered this track to subsequent ripples
2206 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2209 // motion on same track
2210 RegionMoveDrag::motion(event, first_move);
2214 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2215 prev_position = where;
2217 // selection encompasses multiple tracks - just drag
2218 // cross-track drags are forbidden
2219 RegionMoveDrag::motion(event, first_move);
2222 if (!_x_constrained) {
2223 prev_amount += amount;
2226 _last_frame_position = after;
2230 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2232 if (!movement_occurred) {
2236 if (was_double_click() && !_views.empty()) {
2237 DraggingView dv = _views.front();
2238 dv.view->show_region_editor ();
2245 _editor->begin_reversible_command(_("Ripple drag"));
2247 // remove the regions being rippled from the dragging view, updating them to
2248 // their new positions
2249 remove_unselected_from_views (prev_amount, true);
2251 if (allow_moves_across_tracks) {
2253 // if regions were dragged across tracks, we've rippled any later
2254 // regions on the track the regions were dragged off, so we need
2255 // to add the original track to the undo record
2256 orig_tav->playlist()->clear_changes();
2257 vector<Command*> cmds;
2258 orig_tav->playlist()->rdiff (cmds);
2259 _editor->session()->add_commands (cmds);
2261 if (prev_tav && prev_tav != orig_tav) {
2262 prev_tav->playlist()->clear_changes();
2263 vector<Command*> cmds;
2264 prev_tav->playlist()->rdiff (cmds);
2265 _editor->session()->add_commands (cmds);
2268 // selection spanned multiple tracks - all will need adding to undo record
2270 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2271 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2273 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2274 (*pi)->clear_changes();
2275 vector<Command*> cmds;
2276 (*pi)->rdiff (cmds);
2277 _editor->session()->add_commands (cmds);
2281 // other modified playlists are added to undo by RegionMoveDrag::finished()
2282 RegionMoveDrag::finished (event, movement_occurred);
2283 _editor->commit_reversible_command();
2287 RegionRippleDrag::aborted (bool movement_occurred)
2289 RegionMoveDrag::aborted (movement_occurred);
2294 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2296 _view (dynamic_cast<MidiTimeAxisView*> (v))
2298 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2304 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2307 _editor->begin_reversible_command (_("create region"));
2308 _region = add_midi_region (_view, false);
2309 _view->playlist()->freeze ();
2312 framepos_t const f = adjusted_current_frame (event);
2313 if (f < grab_frame()) {
2314 _region->set_initial_position (f);
2317 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2318 so that if this region is duplicated, its duplicate starts on
2319 a snap point rather than 1 frame after a snap point. Otherwise things get
2320 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2321 place snapped notes at the start of the region.
2324 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2325 _region->set_length (len < 1 ? 1 : len);
2331 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2333 if (!movement_occurred) {
2334 add_midi_region (_view, true);
2336 _view->playlist()->thaw ();
2337 _editor->commit_reversible_command();
2342 RegionCreateDrag::aborted (bool)
2345 _view->playlist()->thaw ();
2351 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2356 , _was_selected (false)
2359 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2363 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2365 Gdk::Cursor* cursor;
2366 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2368 float x_fraction = cnote->mouse_x_fraction ();
2370 if (x_fraction > 0.0 && x_fraction < 0.25) {
2371 cursor = _editor->cursors()->left_side_trim;
2374 cursor = _editor->cursors()->right_side_trim;
2378 Drag::start_grab (event, cursor);
2380 region = &cnote->region_view();
2383 temp = region->snap_to_pixel (cnote->x0 (), true);
2384 _snap_delta = temp - cnote->x0 ();
2388 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2393 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2394 if (ms.size() > 1) {
2395 /* has to be relative, may make no sense otherwise */
2399 if (!(_was_selected = cnote->selected())) {
2401 /* tertiary-click means extend selection - we'll do that on button release,
2402 so don't add it here, because otherwise we make it hard to figure
2403 out the "extend-to" range.
2406 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2409 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2412 region->note_selected (cnote, true);
2414 _editor->get_selection().clear_points();
2415 region->unique_select (cnote);
2422 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2424 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2426 _editor->begin_reversible_command (_("resize notes"));
2428 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2429 MidiRegionSelection::iterator next;
2432 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2434 mrv->begin_resizing (at_front);
2440 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2441 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2443 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2447 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2449 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2450 if (_editor->snap_mode () != SnapOff) {
2454 if (_editor->snap_mode () == SnapOff) {
2456 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2457 if (apply_snap_delta) {
2463 if (apply_snap_delta) {
2467 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2473 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2475 if (!movement_occurred) {
2476 /* no motion - select note */
2477 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2478 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2479 _editor->current_mouse_mode() == Editing::MouseDraw) {
2481 bool changed = false;
2483 if (_was_selected) {
2484 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2486 region->note_deselected (cnote);
2489 _editor->get_selection().clear_points();
2490 region->unique_select (cnote);
2494 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2495 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2497 if (!extend && !add && region->selection_size() > 1) {
2498 _editor->get_selection().clear_points();
2499 region->unique_select (cnote);
2501 } else if (extend) {
2502 region->note_selected (cnote, true, true);
2505 /* it was added during button press */
2511 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2512 _editor->commit_reversible_selection_op();
2519 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2520 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2521 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2523 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2526 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2528 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2529 if (_editor->snap_mode () != SnapOff) {
2533 if (_editor->snap_mode () == SnapOff) {
2535 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2536 if (apply_snap_delta) {
2542 if (apply_snap_delta) {
2546 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2550 _editor->commit_reversible_command ();
2554 NoteResizeDrag::aborted (bool)
2556 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2557 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2558 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2560 mrv->abort_resizing ();
2565 AVDraggingView::AVDraggingView (RegionView* v)
2568 initial_position = v->region()->position ();
2571 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2574 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2577 TrackViewList empty;
2579 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2580 std::list<RegionView*> views = rs.by_layer();
2583 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2584 RegionView* rv = (*i);
2585 if (!rv->region()->video_locked()) {
2588 if (rv->region()->locked()) {
2591 _views.push_back (AVDraggingView (rv));
2596 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2598 Drag::start_grab (event);
2599 if (_editor->session() == 0) {
2603 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2609 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2613 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2614 _max_backwards_drag = (
2615 ARDOUR_UI::instance()->video_timeline->get_duration()
2616 + ARDOUR_UI::instance()->video_timeline->get_offset()
2617 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2620 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2621 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2622 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2625 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2628 Timecode::Time timecode;
2629 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2630 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);
2631 show_verbose_cursor_text (buf);
2635 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2637 if (_editor->session() == 0) {
2640 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2644 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2648 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2649 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2651 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2652 dt = - _max_backwards_drag;
2655 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2656 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2658 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2659 RegionView* rv = i->view;
2660 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2663 rv->region()->clear_changes ();
2664 rv->region()->suspend_property_changes();
2666 rv->region()->set_position(i->initial_position + dt);
2667 rv->region_changed(ARDOUR::Properties::position);
2670 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2671 Timecode::Time timecode;
2672 Timecode::Time timediff;
2674 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2675 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2676 snprintf (buf, sizeof (buf),
2677 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2678 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2679 , _("Video Start:"),
2680 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2682 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2684 show_verbose_cursor_text (buf);
2688 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2690 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2697 if (!movement_occurred || ! _editor->session()) {
2701 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2703 _editor->begin_reversible_command (_("Move Video"));
2705 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2706 ARDOUR_UI::instance()->video_timeline->save_undo();
2707 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2708 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2710 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2711 i->view->drag_end();
2712 i->view->region()->resume_property_changes ();
2714 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2717 _editor->session()->maybe_update_session_range(
2718 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2719 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2723 _editor->commit_reversible_command ();
2727 VideoTimeLineDrag::aborted (bool)
2729 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2732 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2733 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2735 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2736 i->view->region()->resume_property_changes ();
2737 i->view->region()->set_position(i->initial_position);
2741 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2742 : RegionDrag (e, i, p, v)
2743 , _operation (StartTrim)
2744 , _preserve_fade_anchor (preserve_fade_anchor)
2745 , _jump_position_when_done (false)
2747 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2751 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2754 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2755 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2757 if (tv && tv->is_track()) {
2758 speed = tv->track()->speed();
2761 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2762 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2763 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2765 framepos_t const pf = adjusted_current_frame (event);
2766 setup_snap_delta (region_start);
2768 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2769 /* Move the contents of the region around without changing the region bounds */
2770 _operation = ContentsTrim;
2771 Drag::start_grab (event, _editor->cursors()->trimmer);
2773 /* These will get overridden for a point trim.*/
2774 if (pf < (region_start + region_length/2)) {
2775 /* closer to front */
2776 _operation = StartTrim;
2777 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2778 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2780 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2784 _operation = EndTrim;
2785 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2786 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2788 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2792 /* jump trim disabled for now
2793 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2794 _jump_position_when_done = true;
2798 switch (_operation) {
2800 show_verbose_cursor_time (region_start);
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 if (_operation == StartTrim) {
2859 rv->trim_front_starting ();
2862 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2865 arv->temporarily_hide_envelope ();
2869 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2870 insert_result = _editor->motion_frozen_playlists.insert (pl);
2872 if (insert_result.second) {
2878 bool non_overlap_trim = false;
2880 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2881 non_overlap_trim = true;
2884 /* contstrain trim to fade length */
2885 if (_preserve_fade_anchor) {
2886 switch (_operation) {
2888 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2889 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2891 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2892 if (ar->locked()) continue;
2893 framecnt_t len = ar->fade_in()->back()->when;
2894 if (len < dt) dt = min(dt, len);
2898 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2899 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2901 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2902 if (ar->locked()) continue;
2903 framecnt_t len = ar->fade_out()->back()->when;
2904 if (len < -dt) dt = max(dt, -len);
2913 switch (_operation) {
2915 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2916 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2917 if (changed && _preserve_fade_anchor) {
2918 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2920 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2921 framecnt_t len = ar->fade_in()->back()->when;
2922 framecnt_t diff = ar->first_frame() - i->initial_position;
2923 framepos_t new_length = len - diff;
2924 i->anchored_fade_length = min (ar->length(), new_length);
2925 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2926 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2933 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2934 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2935 if (changed && _preserve_fade_anchor) {
2936 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2938 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2939 framecnt_t len = ar->fade_out()->back()->when;
2940 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2941 framepos_t new_length = len + diff;
2942 i->anchored_fade_length = min (ar->length(), new_length);
2943 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2944 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2952 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2954 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2955 i->view->move_contents (frame_delta);
2961 switch (_operation) {
2963 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2966 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2969 // show_verbose_cursor_time (frame_delta);
2975 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2977 if (movement_occurred) {
2978 motion (event, false);
2980 if (_operation == StartTrim) {
2981 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2983 /* This must happen before the region's StatefulDiffCommand is created, as it may
2984 `correct' (ahem) the region's _start from being negative to being zero. It
2985 needs to be zero in the undo record.
2987 i->view->trim_front_ending ();
2989 if (_preserve_fade_anchor) {
2990 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2992 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2993 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2994 ar->set_fade_in_length(i->anchored_fade_length);
2995 ar->set_fade_in_active(true);
2998 if (_jump_position_when_done) {
2999 i->view->region()->set_position (i->initial_position);
3002 } else if (_operation == EndTrim) {
3003 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3004 if (_preserve_fade_anchor) {
3005 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3007 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3008 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3009 ar->set_fade_out_length(i->anchored_fade_length);
3010 ar->set_fade_out_active(true);
3013 if (_jump_position_when_done) {
3014 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3019 if (!_views.empty()) {
3020 if (_operation == StartTrim) {
3021 _editor->maybe_locate_with_edit_preroll(
3022 _views.begin()->view->region()->position());
3024 if (_operation == EndTrim) {
3025 _editor->maybe_locate_with_edit_preroll(
3026 _views.begin()->view->region()->position() +
3027 _views.begin()->view->region()->length());
3031 if (!_editor->selection->selected (_primary)) {
3032 _primary->thaw_after_trim ();
3035 set<boost::shared_ptr<Playlist> > diffed_playlists;
3037 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3038 i->view->thaw_after_trim ();
3039 i->view->enable_display (true);
3041 /* Trimming one region may affect others on the playlist, so we need
3042 to get undo Commands from the whole playlist rather than just the
3043 region. Use diffed_playlists to make sure we don't diff a given
3044 playlist more than once.
3046 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3047 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3048 vector<Command*> cmds;
3050 _editor->session()->add_commands (cmds);
3051 diffed_playlists.insert (p);
3056 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3060 _editor->motion_frozen_playlists.clear ();
3061 _editor->commit_reversible_command();
3064 /* no mouse movement */
3065 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3066 _editor->point_trim (event, adjusted_current_frame (event));
3070 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3071 i->view->region()->resume_property_changes ();
3076 TrimDrag::aborted (bool movement_occurred)
3078 /* Our motion method is changing model state, so use the Undo system
3079 to cancel. Perhaps not ideal, as this will leave an Undo point
3080 behind which may be slightly odd from the user's point of view.
3085 if (movement_occurred) {
3089 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3090 i->view->region()->resume_property_changes ();
3095 TrimDrag::setup_pointer_frame_offset ()
3097 list<DraggingView>::iterator i = _views.begin ();
3098 while (i != _views.end() && i->view != _primary) {
3102 if (i == _views.end()) {
3106 switch (_operation) {
3108 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3111 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3118 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3123 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3124 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3129 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3131 Drag::start_grab (event, cursor);
3132 show_verbose_cursor_time (adjusted_current_frame(event));
3136 MeterMarkerDrag::setup_pointer_frame_offset ()
3138 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3142 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3144 if (!_marker->meter().movable()) {
3150 // create a dummy marker for visual representation of moving the
3151 // section, because whether its a copy or not, we're going to
3152 // leave or lose the original marker (leave if its a copy; lose if its
3153 // not, because we'll remove it from the map).
3155 MeterSection section (_marker->meter());
3157 if (!section.movable()) {
3162 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3164 _marker = new MeterMarker (
3166 *_editor->meter_group,
3167 UIConfiguration::instance().color ("meter marker"),
3169 *new MeterSection (_marker->meter())
3172 /* use the new marker for the grab */
3173 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3176 TempoMap& map (_editor->session()->tempo_map());
3177 /* get current state */
3178 before_state = &map.get_state();
3179 /* remove the section while we drag it */
3180 map.remove_meter (section, true);
3184 framepos_t const pf = adjusted_current_frame (event);
3186 _marker->set_position (pf);
3187 show_verbose_cursor_time (pf);
3191 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3193 if (!movement_occurred) {
3194 if (was_double_click()) {
3195 _editor->edit_meter_marker (*_marker);
3200 if (!_marker->meter().movable()) {
3204 motion (event, false);
3206 Timecode::BBT_Time when;
3208 TempoMap& map (_editor->session()->tempo_map());
3209 map.bbt_time (_marker->position(), when);
3211 if (_copy == true) {
3212 _editor->begin_reversible_command (_("copy meter mark"));
3213 XMLNode &before = map.get_state();
3214 map.add_meter (_marker->meter(), when);
3215 XMLNode &after = map.get_state();
3216 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3217 _editor->commit_reversible_command ();
3220 _editor->begin_reversible_command (_("move meter mark"));
3222 /* we removed it before, so add it back now */
3224 map.add_meter (_marker->meter(), when);
3225 XMLNode &after = map.get_state();
3226 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3227 _editor->commit_reversible_command ();
3230 // delete the dummy marker we used for visual representation while moving.
3231 // a new visual marker will show up automatically.
3236 MeterMarkerDrag::aborted (bool moved)
3238 _marker->set_position (_marker->meter().frame ());
3241 TempoMap& map (_editor->session()->tempo_map());
3242 /* we removed it before, so add it back now */
3243 map.add_meter (_marker->meter(), _marker->meter().frame());
3244 // delete the dummy marker we used for visual representation while moving.
3245 // a new visual marker will show up automatically.
3250 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3255 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3257 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3262 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3264 Drag::start_grab (event, cursor);
3265 show_verbose_cursor_time (adjusted_current_frame (event));
3269 TempoMarkerDrag::setup_pointer_frame_offset ()
3271 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3275 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3277 if (!_marker->tempo().movable()) {
3283 // create a dummy marker for visual representation of moving the
3284 // section, because whether its a copy or not, we're going to
3285 // leave or lose the original marker (leave if its a copy; lose if its
3286 // not, because we'll remove it from the map).
3288 // create a dummy marker for visual representation of moving the copy.
3289 // The actual copying is not done before we reach the finish callback.
3292 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3294 TempoSection section (_marker->tempo());
3296 _marker = new TempoMarker (
3298 *_editor->tempo_group,
3299 UIConfiguration::instance().color ("tempo marker"),
3301 *new TempoSection (_marker->tempo())
3304 /* use the new marker for the grab */
3305 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3308 _editor->begin_reversible_command (_("move tempo mark"));
3309 TempoMap& map (_editor->session()->tempo_map());
3310 /* get current state */
3311 before_state = &map.get_state();
3312 /* remove the section while we drag it */
3313 map.remove_tempo (section, true);
3317 framepos_t const pf = adjusted_current_frame (event);
3318 _marker->set_position (pf);
3319 show_verbose_cursor_time (pf);
3323 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3325 if (!movement_occurred) {
3326 if (was_double_click()) {
3327 _editor->edit_tempo_marker (*_marker);
3332 if (!_marker->tempo().movable()) {
3336 motion (event, false);
3338 TempoMap& map (_editor->session()->tempo_map());
3339 framepos_t beat_time = map.round_to_beat (_marker->position(), RoundNearest);
3340 Timecode::BBT_Time when;
3342 map.bbt_time (beat_time, when);
3344 if (_copy == true) {
3345 _editor->begin_reversible_command (_("copy tempo mark"));
3346 XMLNode &before = map.get_state();
3347 map.add_tempo (_marker->tempo(), when);
3348 XMLNode &after = map.get_state();
3349 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3350 _editor->commit_reversible_command ();
3353 /* we removed it before, so add it back now */
3354 map.add_tempo (_marker->tempo(), when);
3355 XMLNode &after = map.get_state();
3356 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3357 _editor->commit_reversible_command ();
3360 // delete the dummy marker we used for visual representation while moving.
3361 // a new visual marker will show up automatically.
3366 TempoMarkerDrag::aborted (bool moved)
3368 _marker->set_position (_marker->tempo().frame());
3370 TempoMap& map (_editor->session()->tempo_map());
3371 /* we removed it before, so add it back now */
3372 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3373 // delete the dummy marker we used for visual representation while moving.
3374 // a new visual marker will show up automatically.
3379 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3380 : Drag (e, &c.track_canvas_item(), false)
3385 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3388 /** Do all the things we do when dragging the playhead to make it look as though
3389 * we have located, without actually doing the locate (because that would cause
3390 * the diskstream buffers to be refilled, which is too slow).
3393 CursorDrag::fake_locate (framepos_t t)
3395 if (_editor->session () == 0) {
3399 _editor->playhead_cursor->set_position (t);
3401 Session* s = _editor->session ();
3402 if (s->timecode_transmission_suspended ()) {
3403 framepos_t const f = _editor->playhead_cursor->current_frame ();
3404 /* This is asynchronous so it will be sent "now"
3406 s->send_mmc_locate (f);
3407 /* These are synchronous and will be sent during the next
3410 s->queue_full_time_code ();
3411 s->queue_song_position_pointer ();
3414 show_verbose_cursor_time (t);
3415 _editor->UpdateAllTransportClocks (t);
3419 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3421 Drag::start_grab (event, c);
3422 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3424 _grab_zoom = _editor->samples_per_pixel;
3426 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3428 _editor->snap_to_with_modifier (where, event);
3430 _editor->_dragging_playhead = true;
3432 Session* s = _editor->session ();
3434 /* grab the track canvas item as well */
3436 _cursor.track_canvas_item().grab();
3439 if (_was_rolling && _stop) {
3443 if (s->is_auditioning()) {
3444 s->cancel_audition ();
3448 if (AudioEngine::instance()->connected()) {
3450 /* do this only if we're the engine is connected
3451 * because otherwise this request will never be
3452 * serviced and we'll busy wait forever. likewise,
3453 * notice if we are disconnected while waiting for the
3454 * request to be serviced.
3457 s->request_suspend_timecode_transmission ();
3458 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3459 /* twiddle our thumbs */
3464 fake_locate (where - snap_delta (event->button.state));
3468 CursorDrag::motion (GdkEvent* event, bool)
3470 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3471 _editor->snap_to_with_modifier (where, event);
3472 if (where != last_pointer_frame()) {
3473 fake_locate (where - snap_delta (event->button.state));
3478 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3480 _editor->_dragging_playhead = false;
3482 _cursor.track_canvas_item().ungrab();
3484 if (!movement_occurred && _stop) {
3488 motion (event, false);
3490 Session* s = _editor->session ();
3492 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3493 _editor->_pending_locate_request = true;
3494 s->request_resume_timecode_transmission ();
3499 CursorDrag::aborted (bool)
3501 _cursor.track_canvas_item().ungrab();
3503 if (_editor->_dragging_playhead) {
3504 _editor->session()->request_resume_timecode_transmission ();
3505 _editor->_dragging_playhead = false;
3508 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3511 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3512 : RegionDrag (e, i, p, v)
3514 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3518 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3520 Drag::start_grab (event, cursor);
3522 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3523 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3524 setup_snap_delta (r->position ());
3526 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3530 FadeInDrag::setup_pointer_frame_offset ()
3532 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3533 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3534 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3538 FadeInDrag::motion (GdkEvent* event, bool)
3540 framecnt_t fade_length;
3542 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3543 _editor->snap_to_with_modifier (pos, event);
3544 pos -= snap_delta (event->button.state);
3546 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3548 if (pos < (region->position() + 64)) {
3549 fade_length = 64; // this should be a minimum defined somewhere
3550 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3551 fade_length = region->length() - region->fade_out()->back()->when - 1;
3553 fade_length = pos - region->position();
3556 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3558 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3564 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3567 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3571 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3573 if (!movement_occurred) {
3577 framecnt_t fade_length;
3578 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3579 _editor->snap_to_with_modifier (pos, event);
3580 pos -= snap_delta (event->button.state);
3582 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3584 if (pos < (region->position() + 64)) {
3585 fade_length = 64; // this should be a minimum defined somewhere
3586 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3587 fade_length = region->length() - region->fade_out()->back()->when - 1;
3589 fade_length = pos - region->position();
3592 bool in_command = false;
3594 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3596 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3602 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3603 XMLNode &before = alist->get_state();
3605 tmp->audio_region()->set_fade_in_length (fade_length);
3606 tmp->audio_region()->set_fade_in_active (true);
3609 _editor->begin_reversible_command (_("change fade in length"));
3612 XMLNode &after = alist->get_state();
3613 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3617 _editor->commit_reversible_command ();
3622 FadeInDrag::aborted (bool)
3624 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3625 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3631 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3635 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3636 : RegionDrag (e, i, p, v)
3638 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3642 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3644 Drag::start_grab (event, cursor);
3646 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3647 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3648 setup_snap_delta (r->last_frame ());
3650 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3654 FadeOutDrag::setup_pointer_frame_offset ()
3656 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3657 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3658 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3662 FadeOutDrag::motion (GdkEvent* event, bool)
3664 framecnt_t fade_length;
3666 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3667 _editor->snap_to_with_modifier (pos, event);
3668 pos -= snap_delta (event->button.state);
3670 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3672 if (pos > (region->last_frame() - 64)) {
3673 fade_length = 64; // this should really be a minimum fade defined somewhere
3674 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3675 fade_length = region->length() - region->fade_in()->back()->when - 1;
3677 fade_length = region->last_frame() - pos;
3680 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3682 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3688 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3691 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3695 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3697 if (!movement_occurred) {
3701 framecnt_t fade_length;
3703 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3704 _editor->snap_to_with_modifier (pos, event);
3705 pos -= snap_delta (event->button.state);
3707 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3709 if (pos > (region->last_frame() - 64)) {
3710 fade_length = 64; // this should really be a minimum fade defined somewhere
3711 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3712 fade_length = region->length() - region->fade_in()->back()->when - 1;
3714 fade_length = region->last_frame() - pos;
3717 bool in_command = false;
3719 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3721 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3727 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3728 XMLNode &before = alist->get_state();
3730 tmp->audio_region()->set_fade_out_length (fade_length);
3731 tmp->audio_region()->set_fade_out_active (true);
3734 _editor->begin_reversible_command (_("change fade out length"));
3737 XMLNode &after = alist->get_state();
3738 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3742 _editor->commit_reversible_command ();
3747 FadeOutDrag::aborted (bool)
3749 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3750 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3756 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3760 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3762 , _selection_changed (false)
3764 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3765 Gtk::Window* toplevel = _editor->current_toplevel();
3766 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3770 _points.push_back (ArdourCanvas::Duple (0, 0));
3772 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3775 MarkerDrag::~MarkerDrag ()
3777 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3782 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3784 location = new Location (*l);
3785 markers.push_back (m);
3790 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3792 Drag::start_grab (event, cursor);
3796 Location *location = _editor->find_location_from_marker (_marker, is_start);
3797 _editor->_dragging_edit_point = true;
3799 update_item (location);
3801 // _drag_line->show();
3802 // _line->raise_to_top();
3805 show_verbose_cursor_time (location->start());
3807 show_verbose_cursor_time (location->end());
3809 setup_snap_delta (is_start ? location->start() : location->end());
3811 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3814 case Selection::Toggle:
3815 /* we toggle on the button release */
3817 case Selection::Set:
3818 if (!_editor->selection->selected (_marker)) {
3819 _editor->selection->set (_marker);
3820 _selection_changed = true;
3823 case Selection::Extend:
3825 Locations::LocationList ll;
3826 list<ArdourMarker*> to_add;
3828 _editor->selection->markers.range (s, e);
3829 s = min (_marker->position(), s);
3830 e = max (_marker->position(), e);
3833 if (e < max_framepos) {
3836 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3837 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3838 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3841 to_add.push_back (lm->start);
3844 to_add.push_back (lm->end);
3848 if (!to_add.empty()) {
3849 _editor->selection->add (to_add);
3850 _selection_changed = true;
3854 case Selection::Add:
3855 _editor->selection->add (_marker);
3856 _selection_changed = true;
3861 /* Set up copies for us to manipulate during the drag
3864 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3866 Location* l = _editor->find_location_from_marker (*i, is_start);
3873 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3875 /* range: check that the other end of the range isn't
3878 CopiedLocationInfo::iterator x;
3879 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3880 if (*(*x).location == *l) {
3884 if (x == _copied_locations.end()) {
3885 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3887 (*x).markers.push_back (*i);
3888 (*x).move_both = true;
3896 MarkerDrag::setup_pointer_frame_offset ()
3899 Location *location = _editor->find_location_from_marker (_marker, is_start);
3900 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3904 MarkerDrag::motion (GdkEvent* event, bool)
3906 framecnt_t f_delta = 0;
3908 bool move_both = false;
3909 Location *real_location;
3910 Location *copy_location = 0;
3911 framecnt_t const sd = snap_delta (event->button.state);
3913 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
3914 framepos_t next = newframe;
3916 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3920 CopiedLocationInfo::iterator x;
3922 /* find the marker we're dragging, and compute the delta */
3924 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3926 copy_location = (*x).location;
3928 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3930 /* this marker is represented by this
3931 * CopiedLocationMarkerInfo
3934 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3939 if (real_location->is_mark()) {
3940 f_delta = newframe - copy_location->start();
3944 switch (_marker->type()) {
3945 case ArdourMarker::SessionStart:
3946 case ArdourMarker::RangeStart:
3947 case ArdourMarker::LoopStart:
3948 case ArdourMarker::PunchIn:
3949 f_delta = newframe - copy_location->start();
3952 case ArdourMarker::SessionEnd:
3953 case ArdourMarker::RangeEnd:
3954 case ArdourMarker::LoopEnd:
3955 case ArdourMarker::PunchOut:
3956 f_delta = newframe - copy_location->end();
3959 /* what kind of marker is this ? */
3968 if (x == _copied_locations.end()) {
3969 /* hmm, impossible - we didn't find the dragged marker */
3973 /* now move them all */
3975 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3977 copy_location = x->location;
3979 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3983 if (real_location->locked()) {
3987 if (copy_location->is_mark()) {
3991 copy_location->set_start (copy_location->start() + f_delta);
3995 framepos_t new_start = copy_location->start() + f_delta;
3996 framepos_t new_end = copy_location->end() + f_delta;
3998 if (is_start) { // start-of-range marker
4000 if (move_both || (*x).move_both) {
4001 copy_location->set_start (new_start);
4002 copy_location->set_end (new_end);
4003 } else if (new_start < copy_location->end()) {
4004 copy_location->set_start (new_start);
4005 } else if (newframe > 0) {
4006 //_editor->snap_to (next, RoundUpAlways, true);
4007 copy_location->set_end (next);
4008 copy_location->set_start (newframe);
4011 } else { // end marker
4013 if (move_both || (*x).move_both) {
4014 copy_location->set_end (new_end);
4015 copy_location->set_start (new_start);
4016 } else if (new_end > copy_location->start()) {
4017 copy_location->set_end (new_end);
4018 } else if (newframe > 0) {
4019 //_editor->snap_to (next, RoundDownAlways, true);
4020 copy_location->set_start (next);
4021 copy_location->set_end (newframe);
4026 update_item (copy_location);
4028 /* now lookup the actual GUI items used to display this
4029 * location and move them to wherever the copy of the location
4030 * is now. This means that the logic in ARDOUR::Location is
4031 * still enforced, even though we are not (yet) modifying
4032 * the real Location itself.
4035 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4038 lm->set_position (copy_location->start(), copy_location->end());
4043 assert (!_copied_locations.empty());
4045 show_verbose_cursor_time (newframe);
4049 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4051 if (!movement_occurred) {
4053 if (was_double_click()) {
4054 _editor->rename_marker (_marker);
4058 /* just a click, do nothing but finish
4059 off the selection process
4062 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4064 case Selection::Set:
4065 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4066 _editor->selection->set (_marker);
4067 _selection_changed = true;
4071 case Selection::Toggle:
4072 /* we toggle on the button release, click only */
4073 _editor->selection->toggle (_marker);
4074 _selection_changed = true;
4078 case Selection::Extend:
4079 case Selection::Add:
4083 if (_selection_changed) {
4084 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4085 _editor->commit_reversible_selection_op();
4091 _editor->_dragging_edit_point = false;
4093 XMLNode &before = _editor->session()->locations()->get_state();
4094 bool in_command = false;
4096 MarkerSelection::iterator i;
4097 CopiedLocationInfo::iterator x;
4100 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4101 x != _copied_locations.end() && i != _editor->selection->markers.end();
4104 Location * location = _editor->find_location_from_marker (*i, is_start);
4108 if (location->locked()) {
4112 _editor->begin_reversible_command ( _("move marker") );
4115 if (location->is_mark()) {
4116 location->set_start (((*x).location)->start());
4118 location->set (((*x).location)->start(), ((*x).location)->end());
4124 XMLNode &after = _editor->session()->locations()->get_state();
4125 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4126 _editor->commit_reversible_command ();
4131 MarkerDrag::aborted (bool movement_occurred)
4133 if (!movement_occurred) {
4137 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4139 /* move all markers to their original location */
4142 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4145 Location * location = _editor->find_location_from_marker (*m, is_start);
4148 (*m)->set_position (is_start ? location->start() : location->end());
4155 MarkerDrag::update_item (Location*)
4160 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4162 , _fixed_grab_x (0.0)
4163 , _fixed_grab_y (0.0)
4164 , _cumulative_x_drag (0.0)
4165 , _cumulative_y_drag (0.0)
4169 if (_zero_gain_fraction < 0.0) {
4170 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4173 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4175 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4181 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4183 Drag::start_grab (event, _editor->cursors()->fader);
4185 // start the grab at the center of the control point so
4186 // the point doesn't 'jump' to the mouse after the first drag
4187 _fixed_grab_x = _point->get_x();
4188 _fixed_grab_y = _point->get_y();
4190 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4191 setup_snap_delta (pos);
4193 float const fraction = 1 - (_point->get_y() / _point->line().height());
4194 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4196 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4198 if (!_point->can_slide ()) {
4199 _x_constrained = true;
4204 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4206 double dx = _drags->current_pointer_x() - last_pointer_x();
4207 double dy = current_pointer_y() - last_pointer_y();
4208 bool need_snap = true;
4210 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4216 /* coordinate in pixels relative to the start of the region (for region-based automation)
4217 or track (for track-based automation) */
4218 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4219 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4221 // calculate zero crossing point. back off by .01 to stay on the
4222 // positive side of zero
4223 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4225 if (_x_constrained) {
4228 if (_y_constrained) {
4232 _cumulative_x_drag = cx - _fixed_grab_x;
4233 _cumulative_y_drag = cy - _fixed_grab_y;
4237 cy = min ((double) _point->line().height(), cy);
4239 // make sure we hit zero when passing through
4240 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4244 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4246 if (!_x_constrained && need_snap) {
4247 _editor->snap_to_with_modifier (cx_frames, event);
4250 cx_frames -= snap_delta (event->button.state);
4251 cx_frames = min (cx_frames, _point->line().maximum_time());
4253 float const fraction = 1.0 - (cy / _point->line().height());
4256 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4257 _editor->begin_reversible_command (_("automation event move"));
4258 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4260 pair<double, float> result;
4261 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4263 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4267 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4269 if (!movement_occurred) {
4272 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4273 _editor->reset_point_selection ();
4277 _point->line().end_drag (_pushing, _final_index);
4278 _editor->commit_reversible_command ();
4283 ControlPointDrag::aborted (bool)
4285 _point->line().reset ();
4289 ControlPointDrag::active (Editing::MouseMode m)
4291 if (m == Editing::MouseDraw) {
4292 /* always active in mouse draw */
4296 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4297 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4300 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4303 , _fixed_grab_x (0.0)
4304 , _fixed_grab_y (0.0)
4305 , _cumulative_y_drag (0)
4309 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4313 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4315 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4318 _item = &_line->grab_item ();
4320 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4321 origin, and ditto for y.
4324 double mx = event->button.x;
4325 double my = event->button.y;
4327 _line->grab_item().canvas_to_item (mx, my);
4329 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4331 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4332 /* no adjacent points */
4336 Drag::start_grab (event, _editor->cursors()->fader);
4338 /* store grab start in item frame */
4339 double const bx = _line->nth (_before)->get_x();
4340 double const ax = _line->nth (_after)->get_x();
4341 double const click_ratio = (ax - mx) / (ax - bx);
4343 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4348 double fraction = 1.0 - (cy / _line->height());
4350 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4354 LineDrag::motion (GdkEvent* event, bool first_move)
4356 double dy = current_pointer_y() - last_pointer_y();
4358 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4362 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4364 _cumulative_y_drag = cy - _fixed_grab_y;
4367 cy = min ((double) _line->height(), cy);
4369 double const fraction = 1.0 - (cy / _line->height());
4373 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4375 _editor->begin_reversible_command (_("automation range move"));
4376 _line->start_drag_line (_before, _after, initial_fraction);
4379 /* we are ignoring x position for this drag, so we can just pass in anything */
4380 pair<double, float> result;
4382 result = _line->drag_motion (0, fraction, true, false, ignored);
4383 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4387 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4389 if (movement_occurred) {
4390 motion (event, false);
4391 _line->end_drag (false, 0);
4392 _editor->commit_reversible_command ();
4394 /* add a new control point on the line */
4396 AutomationTimeAxisView* atv;
4398 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4399 framepos_t where = grab_frame ();
4402 double cy = _fixed_grab_y;
4404 _line->grab_item().item_to_canvas (cx, cy);
4406 atv->add_automation_event (event, where, cy, false);
4407 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4408 AudioRegionView* arv;
4410 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4411 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4418 LineDrag::aborted (bool)
4423 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4427 _region_view_grab_x (0.0),
4428 _cumulative_x_drag (0),
4432 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4436 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4438 Drag::start_grab (event);
4440 _line = reinterpret_cast<Line*> (_item);
4443 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4445 double cx = event->button.x;
4446 double cy = event->button.y;
4448 _item->parent()->canvas_to_item (cx, cy);
4450 /* store grab start in parent frame */
4451 _region_view_grab_x = cx;
4453 _before = *(float*) _item->get_data ("position");
4455 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4457 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4461 FeatureLineDrag::motion (GdkEvent*, bool)
4463 double dx = _drags->current_pointer_x() - last_pointer_x();
4465 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4467 _cumulative_x_drag += dx;
4469 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4478 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4480 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4482 float *pos = new float;
4485 _line->set_data ("position", pos);
4491 FeatureLineDrag::finished (GdkEvent*, bool)
4493 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4494 _arv->update_transient(_before, _before);
4498 FeatureLineDrag::aborted (bool)
4503 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4505 , _vertical_only (false)
4507 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4511 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4513 Drag::start_grab (event);
4514 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4518 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4525 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4527 framepos_t grab = grab_frame ();
4528 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4529 _editor->snap_to_with_modifier (grab, event);
4531 grab = raw_grab_frame ();
4534 /* base start and end on initial click position */
4544 if (current_pointer_y() < grab_y()) {
4545 y1 = current_pointer_y();
4548 y2 = current_pointer_y();
4552 if (start != end || y1 != y2) {
4554 double x1 = _editor->sample_to_pixel (start);
4555 double x2 = _editor->sample_to_pixel (end);
4556 const double min_dimension = 2.0;
4558 if (_vertical_only) {
4559 /* fixed 10 pixel width */
4563 x2 = min (x1 - min_dimension, x2);
4565 x2 = max (x1 + min_dimension, x2);
4570 y2 = min (y1 - min_dimension, y2);
4572 y2 = max (y1 + min_dimension, y2);
4575 /* translate rect into item space and set */
4577 ArdourCanvas::Rect r (x1, y1, x2, y2);
4579 /* this drag is a _trackview_only == true drag, so the y1 and
4580 * y2 (computed using current_pointer_y() and grab_y()) will be
4581 * relative to the top of the trackview group). The
4582 * rubberband rect has the same parent/scroll offset as the
4583 * the trackview group, so we can use the "r" rect directly
4584 * to set the shape of the rubberband.
4587 _editor->rubberband_rect->set (r);
4588 _editor->rubberband_rect->show();
4589 _editor->rubberband_rect->raise_to_top();
4591 show_verbose_cursor_time (pf);
4593 do_select_things (event, true);
4598 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4602 framepos_t grab = grab_frame ();
4603 framepos_t lpf = last_pointer_frame ();
4605 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4606 grab = raw_grab_frame ();
4607 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4621 if (current_pointer_y() < grab_y()) {
4622 y1 = current_pointer_y();
4625 y2 = current_pointer_y();
4629 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4633 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4635 if (movement_occurred) {
4637 motion (event, false);
4638 do_select_things (event, false);
4644 bool do_deselect = true;
4645 MidiTimeAxisView* mtv;
4647 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4649 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4650 /* nothing selected */
4651 add_midi_region (mtv, true);
4652 do_deselect = false;
4656 /* do not deselect if Primary or Tertiary (toggle-select or
4657 * extend-select are pressed.
4660 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4661 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4668 _editor->rubberband_rect->hide();
4672 RubberbandSelectDrag::aborted (bool)
4674 _editor->rubberband_rect->hide ();
4677 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4678 : RegionDrag (e, i, p, v)
4680 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4684 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4686 Drag::start_grab (event, cursor);
4688 _editor->get_selection().add (_primary);
4690 framepos_t where = _primary->region()->position();
4691 setup_snap_delta (where);
4693 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4697 TimeFXDrag::motion (GdkEvent* event, bool)
4699 RegionView* rv = _primary;
4700 StreamView* cv = rv->get_time_axis_view().view ();
4702 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4703 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4704 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4705 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4706 _editor->snap_to_with_modifier (pf, event);
4707 pf -= snap_delta (event->button.state);
4709 if (pf > rv->region()->position()) {
4710 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4713 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4717 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4719 /* this may have been a single click, no drag. We still want the dialog
4720 to show up in that case, so that the user can manually edit the
4721 parameters for the timestretch.
4724 float fraction = 1.0;
4726 if (movement_occurred) {
4728 motion (event, false);
4730 _primary->get_time_axis_view().hide_timestretch ();
4732 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4734 if (adjusted_frame_pos < _primary->region()->position()) {
4735 /* backwards drag of the left edge - not usable */
4739 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4741 fraction = (double) newlen / (double) _primary->region()->length();
4743 #ifndef USE_RUBBERBAND
4744 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4745 if (_primary->region()->data_type() == DataType::AUDIO) {
4746 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4751 if (!_editor->get_selection().regions.empty()) {
4752 /* primary will already be included in the selection, and edit
4753 group shared editing will propagate selection across
4754 equivalent regions, so just use the current region
4758 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4759 error << _("An error occurred while executing time stretch operation") << endmsg;
4765 TimeFXDrag::aborted (bool)
4767 _primary->get_time_axis_view().hide_timestretch ();
4770 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4773 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4777 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4779 Drag::start_grab (event);
4783 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4785 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4789 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4791 if (movement_occurred && _editor->session()) {
4792 /* make sure we stop */
4793 _editor->session()->request_transport_speed (0.0);
4798 ScrubDrag::aborted (bool)
4803 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4807 , _time_selection_at_start (!_editor->get_selection().time.empty())
4809 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4811 if (_time_selection_at_start) {
4812 start_at_start = _editor->get_selection().time.start();
4813 end_at_start = _editor->get_selection().time.end_frame();
4818 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4820 if (_editor->session() == 0) {
4824 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4826 switch (_operation) {
4827 case CreateSelection:
4828 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4833 cursor = _editor->cursors()->selector;
4834 Drag::start_grab (event, cursor);
4837 case SelectionStartTrim:
4838 if (_editor->clicked_axisview) {
4839 _editor->clicked_axisview->order_selection_trims (_item, true);
4841 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4844 case SelectionEndTrim:
4845 if (_editor->clicked_axisview) {
4846 _editor->clicked_axisview->order_selection_trims (_item, false);
4848 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4852 Drag::start_grab (event, cursor);
4855 case SelectionExtend:
4856 Drag::start_grab (event, cursor);
4860 if (_operation == SelectionMove) {
4861 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4863 show_verbose_cursor_time (adjusted_current_frame (event));
4868 SelectionDrag::setup_pointer_frame_offset ()
4870 switch (_operation) {
4871 case CreateSelection:
4872 _pointer_frame_offset = 0;
4875 case SelectionStartTrim:
4877 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4880 case SelectionEndTrim:
4881 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4884 case SelectionExtend:
4890 SelectionDrag::motion (GdkEvent* event, bool first_move)
4892 framepos_t start = 0;
4894 framecnt_t length = 0;
4895 framecnt_t distance = 0;
4897 framepos_t const pending_position = adjusted_current_frame (event);
4899 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4903 switch (_operation) {
4904 case CreateSelection:
4906 framepos_t grab = grab_frame ();
4909 grab = adjusted_current_frame (event, false);
4910 if (grab < pending_position) {
4911 _editor->snap_to (grab, RoundDownMaybe);
4913 _editor->snap_to (grab, RoundUpMaybe);
4917 if (pending_position < grab) {
4918 start = pending_position;
4921 end = pending_position;
4925 /* first drag: Either add to the selection
4926 or create a new selection
4933 /* adding to the selection */
4934 _editor->set_selected_track_as_side_effect (Selection::Add);
4935 _editor->clicked_selection = _editor->selection->add (start, end);
4942 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4943 _editor->set_selected_track_as_side_effect (Selection::Set);
4946 _editor->clicked_selection = _editor->selection->set (start, end);
4950 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4951 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4952 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4954 _editor->selection->add (atest);
4958 /* select all tracks within the rectangle that we've marked out so far */
4959 TrackViewList new_selection;
4960 TrackViewList& all_tracks (_editor->track_views);
4962 ArdourCanvas::Coord const top = grab_y();
4963 ArdourCanvas::Coord const bottom = current_pointer_y();
4965 if (top >= 0 && bottom >= 0) {
4967 //first, find the tracks that are covered in the y range selection
4968 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4969 if ((*i)->covered_by_y_range (top, bottom)) {
4970 new_selection.push_back (*i);
4974 //now find any tracks that are GROUPED with the tracks we selected
4975 TrackViewList grouped_add = new_selection;
4976 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4977 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4978 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4979 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4980 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4981 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4982 grouped_add.push_back (*j);
4987 //now compare our list with the current selection, and add or remove as necessary
4988 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4989 TrackViewList tracks_to_add;
4990 TrackViewList tracks_to_remove;
4991 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4992 if ( !_editor->selection->tracks.contains ( *i ) )
4993 tracks_to_add.push_back ( *i );
4994 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4995 if ( !grouped_add.contains ( *i ) )
4996 tracks_to_remove.push_back ( *i );
4997 _editor->selection->add(tracks_to_add);
4998 _editor->selection->remove(tracks_to_remove);
5004 case SelectionStartTrim:
5006 end = _editor->selection->time[_editor->clicked_selection].end;
5008 if (pending_position > end) {
5011 start = pending_position;
5015 case SelectionEndTrim:
5017 start = _editor->selection->time[_editor->clicked_selection].start;
5019 if (pending_position < start) {
5022 end = pending_position;
5029 start = _editor->selection->time[_editor->clicked_selection].start;
5030 end = _editor->selection->time[_editor->clicked_selection].end;
5032 length = end - start;
5033 distance = pending_position - start;
5034 start = pending_position;
5035 _editor->snap_to (start);
5037 end = start + length;
5041 case SelectionExtend:
5046 switch (_operation) {
5048 if (_time_selection_at_start) {
5049 _editor->selection->move_time (distance);
5053 _editor->selection->replace (_editor->clicked_selection, start, end);
5057 if (_operation == SelectionMove) {
5058 show_verbose_cursor_time(start);
5060 show_verbose_cursor_time(pending_position);
5065 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5067 Session* s = _editor->session();
5069 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5070 if (movement_occurred) {
5071 motion (event, false);
5072 /* XXX this is not object-oriented programming at all. ick */
5073 if (_editor->selection->time.consolidate()) {
5074 _editor->selection->TimeChanged ();
5077 /* XXX what if its a music time selection? */
5079 if (s->get_play_range() && s->transport_rolling()) {
5080 s->request_play_range (&_editor->selection->time, true);
5081 } else if (!s->config.get_external_sync()) {
5082 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5083 if (_operation == SelectionEndTrim)
5084 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5086 s->request_locate (_editor->get_selection().time.start());
5090 if (_editor->get_selection().time.length() != 0) {
5091 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5093 s->clear_range_selection ();
5098 /* just a click, no pointer movement.
5101 if (_operation == SelectionExtend) {
5102 if (_time_selection_at_start) {
5103 framepos_t pos = adjusted_current_frame (event, false);
5104 framepos_t start = min (pos, start_at_start);
5105 framepos_t end = max (pos, end_at_start);
5106 _editor->selection->set (start, end);
5109 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5110 if (_editor->clicked_selection) {
5111 _editor->selection->remove (_editor->clicked_selection);
5114 if (!_editor->clicked_selection) {
5115 _editor->selection->clear_time();
5120 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5121 _editor->selection->set (_editor->clicked_axisview);
5124 if (s && s->get_play_range () && s->transport_rolling()) {
5125 s->request_stop (false, false);
5130 _editor->stop_canvas_autoscroll ();
5131 _editor->clicked_selection = 0;
5132 _editor->commit_reversible_selection_op ();
5136 SelectionDrag::aborted (bool)
5141 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5142 : Drag (e, i, false),
5146 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5148 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5149 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5150 physical_screen_height (_editor->current_toplevel()->get_window())));
5151 _drag_rect->hide ();
5153 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5154 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5157 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5159 /* normal canvas items will be cleaned up when their parent group is deleted. But
5160 this item is created as the child of a long-lived parent group, and so we
5161 need to explicitly delete it.
5167 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5169 if (_editor->session() == 0) {
5173 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5175 if (!_editor->temp_location) {
5176 _editor->temp_location = new Location (*_editor->session());
5179 switch (_operation) {
5180 case CreateSkipMarker:
5181 case CreateRangeMarker:
5182 case CreateTransportMarker:
5183 case CreateCDMarker:
5185 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5190 cursor = _editor->cursors()->selector;
5194 Drag::start_grab (event, cursor);
5196 show_verbose_cursor_time (adjusted_current_frame (event));
5200 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5202 framepos_t start = 0;
5204 ArdourCanvas::Rectangle *crect;
5206 switch (_operation) {
5207 case CreateSkipMarker:
5208 crect = _editor->range_bar_drag_rect;
5210 case CreateRangeMarker:
5211 crect = _editor->range_bar_drag_rect;
5213 case CreateTransportMarker:
5214 crect = _editor->transport_bar_drag_rect;
5216 case CreateCDMarker:
5217 crect = _editor->cd_marker_bar_drag_rect;
5220 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5225 framepos_t const pf = adjusted_current_frame (event);
5227 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5228 framepos_t grab = grab_frame ();
5229 _editor->snap_to (grab);
5231 if (pf < grab_frame()) {
5239 /* first drag: Either add to the selection
5240 or create a new selection.
5245 _editor->temp_location->set (start, end);
5249 update_item (_editor->temp_location);
5251 //_drag_rect->raise_to_top();
5257 _editor->temp_location->set (start, end);
5259 double x1 = _editor->sample_to_pixel (start);
5260 double x2 = _editor->sample_to_pixel (end);
5264 update_item (_editor->temp_location);
5267 show_verbose_cursor_time (pf);
5272 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5274 Location * newloc = 0;
5278 if (movement_occurred) {
5279 motion (event, false);
5282 switch (_operation) {
5283 case CreateSkipMarker:
5284 case CreateRangeMarker:
5285 case CreateCDMarker:
5287 XMLNode &before = _editor->session()->locations()->get_state();
5288 if (_operation == CreateSkipMarker) {
5289 _editor->begin_reversible_command (_("new skip marker"));
5290 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5291 flags = Location::IsRangeMarker | Location::IsSkip;
5292 _editor->range_bar_drag_rect->hide();
5293 } else if (_operation == CreateCDMarker) {
5294 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5295 _editor->begin_reversible_command (_("new CD marker"));
5296 flags = Location::IsRangeMarker | Location::IsCDMarker;
5297 _editor->cd_marker_bar_drag_rect->hide();
5299 _editor->begin_reversible_command (_("new skip marker"));
5300 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5301 flags = Location::IsRangeMarker;
5302 _editor->range_bar_drag_rect->hide();
5304 newloc = new Location (
5305 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5308 _editor->session()->locations()->add (newloc, true);
5309 XMLNode &after = _editor->session()->locations()->get_state();
5310 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5311 _editor->commit_reversible_command ();
5315 case CreateTransportMarker:
5316 // popup menu to pick loop or punch
5317 _editor->new_transport_marker_context_menu (&event->button, _item);
5323 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5325 if (_operation == CreateTransportMarker) {
5327 /* didn't drag, so just locate */
5329 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5331 } else if (_operation == CreateCDMarker) {
5333 /* didn't drag, but mark is already created so do
5336 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5341 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5343 if (end == max_framepos) {
5344 end = _editor->session()->current_end_frame ();
5347 if (start == max_framepos) {
5348 start = _editor->session()->current_start_frame ();
5351 switch (_editor->mouse_mode) {
5353 /* find the two markers on either side and then make the selection from it */
5354 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5358 /* find the two markers on either side of the click and make the range out of it */
5359 _editor->selection->set (start, end);
5368 _editor->stop_canvas_autoscroll ();
5372 RangeMarkerBarDrag::aborted (bool movement_occurred)
5374 if (movement_occurred) {
5375 _drag_rect->hide ();
5380 RangeMarkerBarDrag::update_item (Location* location)
5382 double const x1 = _editor->sample_to_pixel (location->start());
5383 double const x2 = _editor->sample_to_pixel (location->end());
5385 _drag_rect->set_x0 (x1);
5386 _drag_rect->set_x1 (x2);
5389 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5391 , _cumulative_dx (0)
5392 , _cumulative_dy (0)
5393 , _was_selected (false)
5395 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5397 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5399 _region = &_primary->region_view ();
5400 _note_height = _region->midi_stream_view()->note_height ();
5404 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5406 Drag::start_grab (event);
5407 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5409 if (!(_was_selected = _primary->selected())) {
5411 /* tertiary-click means extend selection - we'll do that on button release,
5412 so don't add it here, because otherwise we make it hard to figure
5413 out the "extend-to" range.
5416 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5419 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5422 _region->note_selected (_primary, true);
5424 _editor->get_selection().clear_points();
5425 _region->unique_select (_primary);
5431 /** @return Current total drag x change in frames */
5433 NoteDrag::total_dx (const guint state) const
5436 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5438 /* primary note time */
5439 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5441 /* new time of the primary note in session frames */
5442 frameoffset_t st = n + dx + snap_delta (state);
5444 framepos_t const rp = _region->region()->position ();
5446 /* prevent the note being dragged earlier than the region's position */
5449 /* possibly snap and return corresponding delta */
5453 if (ArdourKeyboard::indicates_snap (state)) {
5454 if (_editor->snap_mode () != SnapOff) {
5458 if (_editor->snap_mode () == SnapOff) {
5460 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5461 if (ArdourKeyboard::indicates_snap_delta (state)) {
5469 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5470 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5472 ret = st - n - snap_delta (state);
5477 /** @return Current total drag y change in note number */
5479 NoteDrag::total_dy () const
5481 MidiStreamView* msv = _region->midi_stream_view ();
5482 double const y = _region->midi_view()->y_position ();
5483 /* new current note */
5484 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5486 n = max (msv->lowest_note(), n);
5487 n = min (msv->highest_note(), n);
5488 /* and work out delta */
5489 return n - msv->y_to_note (grab_y() - y);
5493 NoteDrag::motion (GdkEvent * event, bool)
5495 /* Total change in x and y since the start of the drag */
5496 frameoffset_t const dx = total_dx (event->button.state);
5497 int8_t const dy = total_dy ();
5499 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5500 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5501 double const tdy = -dy * _note_height - _cumulative_dy;
5504 _cumulative_dx += tdx;
5505 _cumulative_dy += tdy;
5507 int8_t note_delta = total_dy();
5509 _region->move_selection (tdx, tdy, note_delta);
5511 /* the new note value may be the same as the old one, but we
5512 * don't know what that means because the selection may have
5513 * involved more than one note and we might be doing something
5514 * odd with them. so show the note value anyway, always.
5517 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5519 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5524 NoteDrag::finished (GdkEvent* ev, bool moved)
5527 /* no motion - select note */
5529 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5530 _editor->current_mouse_mode() == Editing::MouseDraw) {
5532 bool changed = false;
5534 if (_was_selected) {
5535 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5537 _region->note_deselected (_primary);
5540 _editor->get_selection().clear_points();
5541 _region->unique_select (_primary);
5545 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5546 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5548 if (!extend && !add && _region->selection_size() > 1) {
5549 _editor->get_selection().clear_points();
5550 _region->unique_select (_primary);
5552 } else if (extend) {
5553 _region->note_selected (_primary, true, true);
5556 /* it was added during button press */
5563 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5564 _editor->commit_reversible_selection_op();
5568 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5573 NoteDrag::aborted (bool)
5578 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5579 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5580 : Drag (editor, atv->base_item ())
5582 , _y_origin (atv->y_position())
5583 , _nothing_to_drag (false)
5585 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5586 setup (atv->lines ());
5589 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5590 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5591 : Drag (editor, rv->get_canvas_group ())
5593 , _y_origin (rv->get_time_axis_view().y_position())
5594 , _nothing_to_drag (false)
5597 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5599 list<boost::shared_ptr<AutomationLine> > lines;
5601 AudioRegionView* audio_view;
5602 AutomationRegionView* automation_view;
5603 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5604 lines.push_back (audio_view->get_gain_line ());
5605 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5606 lines.push_back (automation_view->line ());
5609 error << _("Automation range drag created for invalid region type") << endmsg;
5615 /** @param lines AutomationLines to drag.
5616 * @param offset Offset from the session start to the points in the AutomationLines.
5619 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5621 /* find the lines that overlap the ranges being dragged */
5622 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5623 while (i != lines.end ()) {
5624 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5627 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5629 /* check this range against all the AudioRanges that we are using */
5630 list<AudioRange>::const_iterator k = _ranges.begin ();
5631 while (k != _ranges.end()) {
5632 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5638 /* add it to our list if it overlaps at all */
5639 if (k != _ranges.end()) {
5644 _lines.push_back (n);
5650 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5654 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5656 return 1.0 - ((global_y - _y_origin) / line->height());
5660 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5662 const double v = list->eval(x);
5663 return _integral ? rint(v) : v;
5667 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5669 Drag::start_grab (event, cursor);
5671 /* Get line states before we start changing things */
5672 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5673 i->state = &i->line->get_state ();
5674 i->original_fraction = y_fraction (i->line, current_pointer_y());
5677 if (_ranges.empty()) {
5679 /* No selected time ranges: drag all points */
5680 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5681 uint32_t const N = i->line->npoints ();
5682 for (uint32_t j = 0; j < N; ++j) {
5683 i->points.push_back (i->line->nth (j));
5689 if (_nothing_to_drag) {
5695 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5697 if (_nothing_to_drag && !first_move) {
5702 _editor->begin_reversible_command (_("automation range move"));
5704 if (!_ranges.empty()) {
5706 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5708 framecnt_t const half = (i->start + i->end) / 2;
5710 /* find the line that this audio range starts in */
5711 list<Line>::iterator j = _lines.begin();
5712 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5716 if (j != _lines.end()) {
5717 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5719 /* j is the line that this audio range starts in; fade into it;
5720 64 samples length plucked out of thin air.
5723 framepos_t a = i->start + 64;
5728 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5729 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5731 XMLNode &before = the_list->get_state();
5732 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5733 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5735 if (add_p || add_q) {
5736 _editor->session()->add_command (
5737 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5741 /* same thing for the end */
5744 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5748 if (j != _lines.end()) {
5749 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5751 /* j is the line that this audio range starts in; fade out of it;
5752 64 samples length plucked out of thin air.
5755 framepos_t b = i->end - 64;
5760 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5761 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5763 XMLNode &before = the_list->get_state();
5764 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5765 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5767 if (add_p || add_q) {
5768 _editor->session()->add_command (
5769 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5774 _nothing_to_drag = true;
5776 /* Find all the points that should be dragged and put them in the relevant
5777 points lists in the Line structs.
5780 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5782 uint32_t const N = i->line->npoints ();
5783 for (uint32_t j = 0; j < N; ++j) {
5785 /* here's a control point on this line */
5786 ControlPoint* p = i->line->nth (j);
5787 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5789 /* see if it's inside a range */
5790 list<AudioRange>::const_iterator k = _ranges.begin ();
5791 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5795 if (k != _ranges.end()) {
5796 /* dragging this point */
5797 _nothing_to_drag = false;
5798 i->points.push_back (p);
5804 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5805 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5809 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5810 float const f = y_fraction (l->line, current_pointer_y());
5811 /* we are ignoring x position for this drag, so we can just pass in anything */
5812 pair<double, float> result;
5814 result = l->line->drag_motion (0, f, true, false, ignored);
5815 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5820 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5822 if (_nothing_to_drag || !motion_occurred) {
5826 motion (event, false);
5827 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5828 i->line->end_drag (false, 0);
5831 _editor->commit_reversible_command ();
5835 AutomationRangeDrag::aborted (bool)
5837 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5842 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5844 , initial_time_axis_view (itav)
5846 /* note that time_axis_view may be null if the regionview was created
5847 * as part of a copy operation.
5849 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5850 layer = v->region()->layer ();
5851 initial_y = v->get_canvas_group()->position().y;
5852 initial_playlist = v->region()->playlist ();
5853 initial_position = v->region()->position ();
5854 initial_end = v->region()->position () + v->region()->length ();
5857 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5858 : Drag (e, i->canvas_item ())
5861 , _cumulative_dx (0)
5863 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5864 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5869 PatchChangeDrag::motion (GdkEvent* ev, bool)
5871 framepos_t f = adjusted_current_frame (ev);
5872 boost::shared_ptr<Region> r = _region_view->region ();
5873 f = max (f, r->position ());
5874 f = min (f, r->last_frame ());
5876 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5877 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5878 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5879 _cumulative_dx = dxu;
5883 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5885 if (!movement_occurred) {
5889 boost::shared_ptr<Region> r (_region_view->region ());
5890 framepos_t f = adjusted_current_frame (ev);
5891 f = max (f, r->position ());
5892 f = min (f, r->last_frame ());
5894 _region_view->move_patch_change (
5896 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5901 PatchChangeDrag::aborted (bool)
5903 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5907 PatchChangeDrag::setup_pointer_frame_offset ()
5909 boost::shared_ptr<Region> region = _region_view->region ();
5910 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5913 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5914 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5921 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5923 _region_view->update_drag_selection (
5925 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5929 MidiRubberbandSelectDrag::deselect_things ()
5934 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5935 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5938 _vertical_only = true;
5942 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5944 double const y = _region_view->midi_view()->y_position ();
5946 y1 = max (0.0, y1 - y);
5947 y2 = max (0.0, y2 - y);
5949 _region_view->update_vertical_drag_selection (
5952 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5957 MidiVerticalSelectDrag::deselect_things ()
5962 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5963 : RubberbandSelectDrag (e, i)
5969 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5971 if (drag_in_progress) {
5972 /* We just want to select things at the end of the drag, not during it */
5976 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5978 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5980 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5982 _editor->commit_reversible_selection_op ();
5986 EditorRubberbandSelectDrag::deselect_things ()
5988 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5990 _editor->selection->clear_tracks();
5991 _editor->selection->clear_regions();
5992 _editor->selection->clear_points ();
5993 _editor->selection->clear_lines ();
5994 _editor->selection->clear_midi_notes ();
5996 _editor->commit_reversible_selection_op();
5999 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6004 _note[0] = _note[1] = 0;
6007 NoteCreateDrag::~NoteCreateDrag ()
6013 NoteCreateDrag::grid_frames (framepos_t t) const
6016 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6018 grid_beats = Evoral::Beats(1);
6021 return _region_view->region_beats_to_region_frames (grid_beats);
6025 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6027 Drag::start_grab (event, cursor);
6029 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6031 framepos_t pf = _drags->current_pointer_frame ();
6032 framecnt_t const g = grid_frames (pf);
6034 /* Hack so that we always snap to the note that we are over, instead of snapping
6035 to the next one if we're more than halfway through the one we're over.
6037 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6041 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6042 _note[1] = _note[0];
6044 MidiStreamView* sv = _region_view->midi_stream_view ();
6045 double const x = _editor->sample_to_pixel (_note[0]);
6046 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6048 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6049 _drag_rect->set_outline_all ();
6050 _drag_rect->set_outline_color (0xffffff99);
6051 _drag_rect->set_fill_color (0xffffff66);
6055 NoteCreateDrag::motion (GdkEvent* event, bool)
6057 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6058 double const x0 = _editor->sample_to_pixel (_note[0]);
6059 double const x1 = _editor->sample_to_pixel (_note[1]);
6060 _drag_rect->set_x0 (std::min(x0, x1));
6061 _drag_rect->set_x1 (std::max(x0, x1));
6065 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6067 if (!had_movement) {
6071 framepos_t const start = min (_note[0], _note[1]);
6072 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6074 framecnt_t const g = grid_frames (start);
6075 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6077 if (_editor->snap_mode() == SnapNormal && length < g) {
6081 Evoral::Beats length_beats = max (
6082 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6084 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6088 NoteCreateDrag::y_to_region (double y) const
6091 _region_view->get_canvas_group()->canvas_to_item (x, y);
6096 NoteCreateDrag::aborted (bool)
6101 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6106 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6110 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6112 Drag::start_grab (event, cursor);
6116 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6122 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6125 distance = _drags->current_pointer_x() - grab_x();
6126 len = ar->fade_in()->back()->when;
6128 distance = grab_x() - _drags->current_pointer_x();
6129 len = ar->fade_out()->back()->when;
6132 /* how long should it be ? */
6134 new_length = len + _editor->pixel_to_sample (distance);
6136 /* now check with the region that this is legal */
6138 new_length = ar->verify_xfade_bounds (new_length, start);
6141 arv->reset_fade_in_shape_width (ar, new_length);
6143 arv->reset_fade_out_shape_width (ar, new_length);
6148 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6154 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6157 distance = _drags->current_pointer_x() - grab_x();
6158 len = ar->fade_in()->back()->when;
6160 distance = grab_x() - _drags->current_pointer_x();
6161 len = ar->fade_out()->back()->when;
6164 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6166 _editor->begin_reversible_command ("xfade trim");
6167 ar->playlist()->clear_owned_changes ();
6170 ar->set_fade_in_length (new_length);
6172 ar->set_fade_out_length (new_length);
6175 /* Adjusting the xfade may affect other regions in the playlist, so we need
6176 to get undo Commands from the whole playlist rather than just the
6180 vector<Command*> cmds;
6181 ar->playlist()->rdiff (cmds);
6182 _editor->session()->add_commands (cmds);
6183 _editor->commit_reversible_command ();
6188 CrossfadeEdgeDrag::aborted (bool)
6191 // arv->redraw_start_xfade ();
6193 // arv->redraw_end_xfade ();
6197 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6198 : Drag (e, item, true)
6199 , line (new EditorCursor (*e))
6201 line->set_position (pos);
6205 RegionCutDrag::~RegionCutDrag ()
6211 RegionCutDrag::motion (GdkEvent*, bool)
6213 framepos_t where = _drags->current_pointer_frame();
6214 _editor->snap_to (where);
6216 line->set_position (where);
6220 RegionCutDrag::finished (GdkEvent*, bool)
6222 _editor->get_track_canvas()->canvas()->re_enter();
6224 framepos_t pos = _drags->current_pointer_frame();
6228 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6234 _editor->split_regions_at (pos, rs);
6238 RegionCutDrag::aborted (bool)