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 if (!movement_occurred) {
4723 motion (event, false);
4725 _primary->get_time_axis_view().hide_timestretch ();
4727 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4729 if (adjusted_frame_pos < _primary->region()->position()) {
4730 /* backwards drag of the left edge - not usable */
4734 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4736 float percentage = (double) newlen / (double) _primary->region()->length();
4738 #ifndef USE_RUBBERBAND
4739 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4740 if (_primary->region()->data_type() == DataType::AUDIO) {
4741 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4745 if (!_editor->get_selection().regions.empty()) {
4746 /* primary will already be included in the selection, and edit
4747 group shared editing will propagate selection across
4748 equivalent regions, so just use the current region
4752 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4753 error << _("An error occurred while executing time stretch operation") << endmsg;
4759 TimeFXDrag::aborted (bool)
4761 _primary->get_time_axis_view().hide_timestretch ();
4764 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4767 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4771 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4773 Drag::start_grab (event);
4777 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4779 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4783 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4785 if (movement_occurred && _editor->session()) {
4786 /* make sure we stop */
4787 _editor->session()->request_transport_speed (0.0);
4792 ScrubDrag::aborted (bool)
4797 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4801 , _time_selection_at_start (!_editor->get_selection().time.empty())
4803 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4805 if (_time_selection_at_start) {
4806 start_at_start = _editor->get_selection().time.start();
4807 end_at_start = _editor->get_selection().time.end_frame();
4812 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4814 if (_editor->session() == 0) {
4818 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4820 switch (_operation) {
4821 case CreateSelection:
4822 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4827 cursor = _editor->cursors()->selector;
4828 Drag::start_grab (event, cursor);
4831 case SelectionStartTrim:
4832 if (_editor->clicked_axisview) {
4833 _editor->clicked_axisview->order_selection_trims (_item, true);
4835 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4838 case SelectionEndTrim:
4839 if (_editor->clicked_axisview) {
4840 _editor->clicked_axisview->order_selection_trims (_item, false);
4842 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4846 Drag::start_grab (event, cursor);
4849 case SelectionExtend:
4850 Drag::start_grab (event, cursor);
4854 if (_operation == SelectionMove) {
4855 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4857 show_verbose_cursor_time (adjusted_current_frame (event));
4862 SelectionDrag::setup_pointer_frame_offset ()
4864 switch (_operation) {
4865 case CreateSelection:
4866 _pointer_frame_offset = 0;
4869 case SelectionStartTrim:
4871 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4874 case SelectionEndTrim:
4875 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4878 case SelectionExtend:
4884 SelectionDrag::motion (GdkEvent* event, bool first_move)
4886 framepos_t start = 0;
4888 framecnt_t length = 0;
4889 framecnt_t distance = 0;
4891 framepos_t const pending_position = adjusted_current_frame (event);
4893 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4897 switch (_operation) {
4898 case CreateSelection:
4900 framepos_t grab = grab_frame ();
4903 grab = adjusted_current_frame (event, false);
4904 if (grab < pending_position) {
4905 _editor->snap_to (grab, RoundDownMaybe);
4907 _editor->snap_to (grab, RoundUpMaybe);
4911 if (pending_position < grab) {
4912 start = pending_position;
4915 end = pending_position;
4919 /* first drag: Either add to the selection
4920 or create a new selection
4927 /* adding to the selection */
4928 _editor->set_selected_track_as_side_effect (Selection::Add);
4929 _editor->clicked_selection = _editor->selection->add (start, end);
4936 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4937 _editor->set_selected_track_as_side_effect (Selection::Set);
4940 _editor->clicked_selection = _editor->selection->set (start, end);
4944 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4945 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4946 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4948 _editor->selection->add (atest);
4952 /* select all tracks within the rectangle that we've marked out so far */
4953 TrackViewList new_selection;
4954 TrackViewList& all_tracks (_editor->track_views);
4956 ArdourCanvas::Coord const top = grab_y();
4957 ArdourCanvas::Coord const bottom = current_pointer_y();
4959 if (top >= 0 && bottom >= 0) {
4961 //first, find the tracks that are covered in the y range selection
4962 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4963 if ((*i)->covered_by_y_range (top, bottom)) {
4964 new_selection.push_back (*i);
4968 //now find any tracks that are GROUPED with the tracks we selected
4969 TrackViewList grouped_add = new_selection;
4970 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4971 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4972 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4973 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4974 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4975 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4976 grouped_add.push_back (*j);
4981 //now compare our list with the current selection, and add or remove as necessary
4982 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4983 TrackViewList tracks_to_add;
4984 TrackViewList tracks_to_remove;
4985 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4986 if ( !_editor->selection->tracks.contains ( *i ) )
4987 tracks_to_add.push_back ( *i );
4988 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4989 if ( !grouped_add.contains ( *i ) )
4990 tracks_to_remove.push_back ( *i );
4991 _editor->selection->add(tracks_to_add);
4992 _editor->selection->remove(tracks_to_remove);
4998 case SelectionStartTrim:
5000 end = _editor->selection->time[_editor->clicked_selection].end;
5002 if (pending_position > end) {
5005 start = pending_position;
5009 case SelectionEndTrim:
5011 start = _editor->selection->time[_editor->clicked_selection].start;
5013 if (pending_position < start) {
5016 end = pending_position;
5023 start = _editor->selection->time[_editor->clicked_selection].start;
5024 end = _editor->selection->time[_editor->clicked_selection].end;
5026 length = end - start;
5027 distance = pending_position - start;
5028 start = pending_position;
5029 _editor->snap_to (start);
5031 end = start + length;
5035 case SelectionExtend:
5040 switch (_operation) {
5042 if (_time_selection_at_start) {
5043 _editor->selection->move_time (distance);
5047 _editor->selection->replace (_editor->clicked_selection, start, end);
5051 if (_operation == SelectionMove) {
5052 show_verbose_cursor_time(start);
5054 show_verbose_cursor_time(pending_position);
5059 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5061 Session* s = _editor->session();
5063 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5064 if (movement_occurred) {
5065 motion (event, false);
5066 /* XXX this is not object-oriented programming at all. ick */
5067 if (_editor->selection->time.consolidate()) {
5068 _editor->selection->TimeChanged ();
5071 /* XXX what if its a music time selection? */
5073 if (s->get_play_range() && s->transport_rolling()) {
5074 s->request_play_range (&_editor->selection->time, true);
5075 } else if (!s->config.get_external_sync()) {
5076 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5077 if (_operation == SelectionEndTrim)
5078 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5080 s->request_locate (_editor->get_selection().time.start());
5084 if (_editor->get_selection().time.length() != 0) {
5085 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5087 s->clear_range_selection ();
5092 /* just a click, no pointer movement.
5095 if (_operation == SelectionExtend) {
5096 if (_time_selection_at_start) {
5097 framepos_t pos = adjusted_current_frame (event, false);
5098 framepos_t start = min (pos, start_at_start);
5099 framepos_t end = max (pos, end_at_start);
5100 _editor->selection->set (start, end);
5103 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5104 if (_editor->clicked_selection) {
5105 _editor->selection->remove (_editor->clicked_selection);
5108 if (!_editor->clicked_selection) {
5109 _editor->selection->clear_time();
5114 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5115 _editor->selection->set (_editor->clicked_axisview);
5118 if (s && s->get_play_range () && s->transport_rolling()) {
5119 s->request_stop (false, false);
5124 _editor->stop_canvas_autoscroll ();
5125 _editor->clicked_selection = 0;
5126 _editor->commit_reversible_selection_op ();
5130 SelectionDrag::aborted (bool)
5135 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5136 : Drag (e, i, false),
5140 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5142 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5143 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5144 physical_screen_height (_editor->current_toplevel()->get_window())));
5145 _drag_rect->hide ();
5147 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5148 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5151 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5153 /* normal canvas items will be cleaned up when their parent group is deleted. But
5154 this item is created as the child of a long-lived parent group, and so we
5155 need to explicitly delete it.
5161 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5163 if (_editor->session() == 0) {
5167 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5169 if (!_editor->temp_location) {
5170 _editor->temp_location = new Location (*_editor->session());
5173 switch (_operation) {
5174 case CreateSkipMarker:
5175 case CreateRangeMarker:
5176 case CreateTransportMarker:
5177 case CreateCDMarker:
5179 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5184 cursor = _editor->cursors()->selector;
5188 Drag::start_grab (event, cursor);
5190 show_verbose_cursor_time (adjusted_current_frame (event));
5194 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5196 framepos_t start = 0;
5198 ArdourCanvas::Rectangle *crect;
5200 switch (_operation) {
5201 case CreateSkipMarker:
5202 crect = _editor->range_bar_drag_rect;
5204 case CreateRangeMarker:
5205 crect = _editor->range_bar_drag_rect;
5207 case CreateTransportMarker:
5208 crect = _editor->transport_bar_drag_rect;
5210 case CreateCDMarker:
5211 crect = _editor->cd_marker_bar_drag_rect;
5214 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5219 framepos_t const pf = adjusted_current_frame (event);
5221 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5222 framepos_t grab = grab_frame ();
5223 _editor->snap_to (grab);
5225 if (pf < grab_frame()) {
5233 /* first drag: Either add to the selection
5234 or create a new selection.
5239 _editor->temp_location->set (start, end);
5243 update_item (_editor->temp_location);
5245 //_drag_rect->raise_to_top();
5251 _editor->temp_location->set (start, end);
5253 double x1 = _editor->sample_to_pixel (start);
5254 double x2 = _editor->sample_to_pixel (end);
5258 update_item (_editor->temp_location);
5261 show_verbose_cursor_time (pf);
5266 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5268 Location * newloc = 0;
5272 if (movement_occurred) {
5273 motion (event, false);
5276 switch (_operation) {
5277 case CreateSkipMarker:
5278 case CreateRangeMarker:
5279 case CreateCDMarker:
5281 XMLNode &before = _editor->session()->locations()->get_state();
5282 if (_operation == CreateSkipMarker) {
5283 _editor->begin_reversible_command (_("new skip marker"));
5284 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5285 flags = Location::IsRangeMarker | Location::IsSkip;
5286 _editor->range_bar_drag_rect->hide();
5287 } else if (_operation == CreateCDMarker) {
5288 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5289 _editor->begin_reversible_command (_("new CD marker"));
5290 flags = Location::IsRangeMarker | Location::IsCDMarker;
5291 _editor->cd_marker_bar_drag_rect->hide();
5293 _editor->begin_reversible_command (_("new skip marker"));
5294 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5295 flags = Location::IsRangeMarker;
5296 _editor->range_bar_drag_rect->hide();
5298 newloc = new Location (
5299 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5302 _editor->session()->locations()->add (newloc, true);
5303 XMLNode &after = _editor->session()->locations()->get_state();
5304 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5305 _editor->commit_reversible_command ();
5309 case CreateTransportMarker:
5310 // popup menu to pick loop or punch
5311 _editor->new_transport_marker_context_menu (&event->button, _item);
5317 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5319 if (_operation == CreateTransportMarker) {
5321 /* didn't drag, so just locate */
5323 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5325 } else if (_operation == CreateCDMarker) {
5327 /* didn't drag, but mark is already created so do
5330 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5335 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5337 if (end == max_framepos) {
5338 end = _editor->session()->current_end_frame ();
5341 if (start == max_framepos) {
5342 start = _editor->session()->current_start_frame ();
5345 switch (_editor->mouse_mode) {
5347 /* find the two markers on either side and then make the selection from it */
5348 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5352 /* find the two markers on either side of the click and make the range out of it */
5353 _editor->selection->set (start, end);
5362 _editor->stop_canvas_autoscroll ();
5366 RangeMarkerBarDrag::aborted (bool movement_occurred)
5368 if (movement_occurred) {
5369 _drag_rect->hide ();
5374 RangeMarkerBarDrag::update_item (Location* location)
5376 double const x1 = _editor->sample_to_pixel (location->start());
5377 double const x2 = _editor->sample_to_pixel (location->end());
5379 _drag_rect->set_x0 (x1);
5380 _drag_rect->set_x1 (x2);
5383 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5385 , _cumulative_dx (0)
5386 , _cumulative_dy (0)
5387 , _was_selected (false)
5389 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5391 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5393 _region = &_primary->region_view ();
5394 _note_height = _region->midi_stream_view()->note_height ();
5398 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5400 Drag::start_grab (event);
5401 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5403 if (!(_was_selected = _primary->selected())) {
5405 /* tertiary-click means extend selection - we'll do that on button release,
5406 so don't add it here, because otherwise we make it hard to figure
5407 out the "extend-to" range.
5410 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5413 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5416 _region->note_selected (_primary, true);
5418 _editor->get_selection().clear_points();
5419 _region->unique_select (_primary);
5425 /** @return Current total drag x change in frames */
5427 NoteDrag::total_dx (const guint state) const
5430 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5432 /* primary note time */
5433 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5435 /* new time of the primary note in session frames */
5436 frameoffset_t st = n + dx + snap_delta (state);
5438 framepos_t const rp = _region->region()->position ();
5440 /* prevent the note being dragged earlier than the region's position */
5443 /* possibly snap and return corresponding delta */
5447 if (ArdourKeyboard::indicates_snap (state)) {
5448 if (_editor->snap_mode () != SnapOff) {
5452 if (_editor->snap_mode () == SnapOff) {
5454 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5455 if (ArdourKeyboard::indicates_snap_delta (state)) {
5463 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5464 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5466 ret = st - n - snap_delta (state);
5471 /** @return Current total drag y change in note number */
5473 NoteDrag::total_dy () const
5475 MidiStreamView* msv = _region->midi_stream_view ();
5476 double const y = _region->midi_view()->y_position ();
5477 /* new current note */
5478 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5480 n = max (msv->lowest_note(), n);
5481 n = min (msv->highest_note(), n);
5482 /* and work out delta */
5483 return n - msv->y_to_note (grab_y() - y);
5487 NoteDrag::motion (GdkEvent * event, bool)
5489 /* Total change in x and y since the start of the drag */
5490 frameoffset_t const dx = total_dx (event->button.state);
5491 int8_t const dy = total_dy ();
5493 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5494 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5495 double const tdy = -dy * _note_height - _cumulative_dy;
5498 _cumulative_dx += tdx;
5499 _cumulative_dy += tdy;
5501 int8_t note_delta = total_dy();
5503 _region->move_selection (tdx, tdy, note_delta);
5505 /* the new note value may be the same as the old one, but we
5506 * don't know what that means because the selection may have
5507 * involved more than one note and we might be doing something
5508 * odd with them. so show the note value anyway, always.
5511 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5513 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5518 NoteDrag::finished (GdkEvent* ev, bool moved)
5521 /* no motion - select note */
5523 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5524 _editor->current_mouse_mode() == Editing::MouseDraw) {
5526 bool changed = false;
5528 if (_was_selected) {
5529 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5531 _region->note_deselected (_primary);
5534 _editor->get_selection().clear_points();
5535 _region->unique_select (_primary);
5539 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5540 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5542 if (!extend && !add && _region->selection_size() > 1) {
5543 _editor->get_selection().clear_points();
5544 _region->unique_select (_primary);
5546 } else if (extend) {
5547 _region->note_selected (_primary, true, true);
5550 /* it was added during button press */
5557 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5558 _editor->commit_reversible_selection_op();
5562 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5567 NoteDrag::aborted (bool)
5572 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5573 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5574 : Drag (editor, atv->base_item ())
5576 , _y_origin (atv->y_position())
5577 , _nothing_to_drag (false)
5579 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5580 setup (atv->lines ());
5583 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5584 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5585 : Drag (editor, rv->get_canvas_group ())
5587 , _y_origin (rv->get_time_axis_view().y_position())
5588 , _nothing_to_drag (false)
5591 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5593 list<boost::shared_ptr<AutomationLine> > lines;
5595 AudioRegionView* audio_view;
5596 AutomationRegionView* automation_view;
5597 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5598 lines.push_back (audio_view->get_gain_line ());
5599 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5600 lines.push_back (automation_view->line ());
5603 error << _("Automation range drag created for invalid region type") << endmsg;
5609 /** @param lines AutomationLines to drag.
5610 * @param offset Offset from the session start to the points in the AutomationLines.
5613 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5615 /* find the lines that overlap the ranges being dragged */
5616 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5617 while (i != lines.end ()) {
5618 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5621 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5623 /* check this range against all the AudioRanges that we are using */
5624 list<AudioRange>::const_iterator k = _ranges.begin ();
5625 while (k != _ranges.end()) {
5626 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5632 /* add it to our list if it overlaps at all */
5633 if (k != _ranges.end()) {
5638 _lines.push_back (n);
5644 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5648 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5650 return 1.0 - ((global_y - _y_origin) / line->height());
5654 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5656 const double v = list->eval(x);
5657 return _integral ? rint(v) : v;
5661 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5663 Drag::start_grab (event, cursor);
5665 /* Get line states before we start changing things */
5666 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5667 i->state = &i->line->get_state ();
5668 i->original_fraction = y_fraction (i->line, current_pointer_y());
5671 if (_ranges.empty()) {
5673 /* No selected time ranges: drag all points */
5674 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5675 uint32_t const N = i->line->npoints ();
5676 for (uint32_t j = 0; j < N; ++j) {
5677 i->points.push_back (i->line->nth (j));
5683 if (_nothing_to_drag) {
5689 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5691 if (_nothing_to_drag && !first_move) {
5696 _editor->begin_reversible_command (_("automation range move"));
5698 if (!_ranges.empty()) {
5700 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5702 framecnt_t const half = (i->start + i->end) / 2;
5704 /* find the line that this audio range starts in */
5705 list<Line>::iterator j = _lines.begin();
5706 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5710 if (j != _lines.end()) {
5711 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5713 /* j is the line that this audio range starts in; fade into it;
5714 64 samples length plucked out of thin air.
5717 framepos_t a = i->start + 64;
5722 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5723 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5725 XMLNode &before = the_list->get_state();
5726 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5727 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5729 if (add_p || add_q) {
5730 _editor->session()->add_command (
5731 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5735 /* same thing for the end */
5738 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5742 if (j != _lines.end()) {
5743 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5745 /* j is the line that this audio range starts in; fade out of it;
5746 64 samples length plucked out of thin air.
5749 framepos_t b = i->end - 64;
5754 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5755 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5757 XMLNode &before = the_list->get_state();
5758 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5759 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5761 if (add_p || add_q) {
5762 _editor->session()->add_command (
5763 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5768 _nothing_to_drag = true;
5770 /* Find all the points that should be dragged and put them in the relevant
5771 points lists in the Line structs.
5774 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5776 uint32_t const N = i->line->npoints ();
5777 for (uint32_t j = 0; j < N; ++j) {
5779 /* here's a control point on this line */
5780 ControlPoint* p = i->line->nth (j);
5781 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5783 /* see if it's inside a range */
5784 list<AudioRange>::const_iterator k = _ranges.begin ();
5785 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5789 if (k != _ranges.end()) {
5790 /* dragging this point */
5791 _nothing_to_drag = false;
5792 i->points.push_back (p);
5798 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5799 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5803 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5804 float const f = y_fraction (l->line, current_pointer_y());
5805 /* we are ignoring x position for this drag, so we can just pass in anything */
5806 pair<double, float> result;
5808 result = l->line->drag_motion (0, f, true, false, ignored);
5809 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5814 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5816 if (_nothing_to_drag || !motion_occurred) {
5820 motion (event, false);
5821 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5822 i->line->end_drag (false, 0);
5825 _editor->commit_reversible_command ();
5829 AutomationRangeDrag::aborted (bool)
5831 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5836 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5838 , initial_time_axis_view (itav)
5840 /* note that time_axis_view may be null if the regionview was created
5841 * as part of a copy operation.
5843 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5844 layer = v->region()->layer ();
5845 initial_y = v->get_canvas_group()->position().y;
5846 initial_playlist = v->region()->playlist ();
5847 initial_position = v->region()->position ();
5848 initial_end = v->region()->position () + v->region()->length ();
5851 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5852 : Drag (e, i->canvas_item ())
5855 , _cumulative_dx (0)
5857 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5858 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5863 PatchChangeDrag::motion (GdkEvent* ev, bool)
5865 framepos_t f = adjusted_current_frame (ev);
5866 boost::shared_ptr<Region> r = _region_view->region ();
5867 f = max (f, r->position ());
5868 f = min (f, r->last_frame ());
5870 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5871 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5872 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5873 _cumulative_dx = dxu;
5877 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5879 if (!movement_occurred) {
5883 boost::shared_ptr<Region> r (_region_view->region ());
5884 framepos_t f = adjusted_current_frame (ev);
5885 f = max (f, r->position ());
5886 f = min (f, r->last_frame ());
5888 _region_view->move_patch_change (
5890 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5895 PatchChangeDrag::aborted (bool)
5897 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5901 PatchChangeDrag::setup_pointer_frame_offset ()
5903 boost::shared_ptr<Region> region = _region_view->region ();
5904 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5907 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5908 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5915 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5917 _region_view->update_drag_selection (
5919 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5923 MidiRubberbandSelectDrag::deselect_things ()
5928 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5929 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5932 _vertical_only = true;
5936 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5938 double const y = _region_view->midi_view()->y_position ();
5940 y1 = max (0.0, y1 - y);
5941 y2 = max (0.0, y2 - y);
5943 _region_view->update_vertical_drag_selection (
5946 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5951 MidiVerticalSelectDrag::deselect_things ()
5956 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5957 : RubberbandSelectDrag (e, i)
5963 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5965 if (drag_in_progress) {
5966 /* We just want to select things at the end of the drag, not during it */
5970 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5972 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5974 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5976 _editor->commit_reversible_selection_op ();
5980 EditorRubberbandSelectDrag::deselect_things ()
5982 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5984 _editor->selection->clear_tracks();
5985 _editor->selection->clear_regions();
5986 _editor->selection->clear_points ();
5987 _editor->selection->clear_lines ();
5988 _editor->selection->clear_midi_notes ();
5990 _editor->commit_reversible_selection_op();
5993 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5998 _note[0] = _note[1] = 0;
6001 NoteCreateDrag::~NoteCreateDrag ()
6007 NoteCreateDrag::grid_frames (framepos_t t) const
6010 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6012 grid_beats = Evoral::Beats(1);
6015 return _region_view->region_beats_to_region_frames (grid_beats);
6019 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6021 Drag::start_grab (event, cursor);
6023 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6025 framepos_t pf = _drags->current_pointer_frame ();
6026 framecnt_t const g = grid_frames (pf);
6028 /* Hack so that we always snap to the note that we are over, instead of snapping
6029 to the next one if we're more than halfway through the one we're over.
6031 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6035 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6036 _note[1] = _note[0];
6038 MidiStreamView* sv = _region_view->midi_stream_view ();
6039 double const x = _editor->sample_to_pixel (_note[0]);
6040 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6042 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6043 _drag_rect->set_outline_all ();
6044 _drag_rect->set_outline_color (0xffffff99);
6045 _drag_rect->set_fill_color (0xffffff66);
6049 NoteCreateDrag::motion (GdkEvent* event, bool)
6051 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6052 double const x0 = _editor->sample_to_pixel (_note[0]);
6053 double const x1 = _editor->sample_to_pixel (_note[1]);
6054 _drag_rect->set_x0 (std::min(x0, x1));
6055 _drag_rect->set_x1 (std::max(x0, x1));
6059 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6061 if (!had_movement) {
6065 framepos_t const start = min (_note[0], _note[1]);
6066 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6068 framecnt_t const g = grid_frames (start);
6069 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6071 if (_editor->snap_mode() == SnapNormal && length < g) {
6075 Evoral::Beats length_beats = max (
6076 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6078 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6082 NoteCreateDrag::y_to_region (double y) const
6085 _region_view->get_canvas_group()->canvas_to_item (x, y);
6090 NoteCreateDrag::aborted (bool)
6095 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6100 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6104 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6106 Drag::start_grab (event, cursor);
6110 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6116 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6119 distance = _drags->current_pointer_x() - grab_x();
6120 len = ar->fade_in()->back()->when;
6122 distance = grab_x() - _drags->current_pointer_x();
6123 len = ar->fade_out()->back()->when;
6126 /* how long should it be ? */
6128 new_length = len + _editor->pixel_to_sample (distance);
6130 /* now check with the region that this is legal */
6132 new_length = ar->verify_xfade_bounds (new_length, start);
6135 arv->reset_fade_in_shape_width (ar, new_length);
6137 arv->reset_fade_out_shape_width (ar, new_length);
6142 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6148 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6151 distance = _drags->current_pointer_x() - grab_x();
6152 len = ar->fade_in()->back()->when;
6154 distance = grab_x() - _drags->current_pointer_x();
6155 len = ar->fade_out()->back()->when;
6158 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6160 _editor->begin_reversible_command ("xfade trim");
6161 ar->playlist()->clear_owned_changes ();
6164 ar->set_fade_in_length (new_length);
6166 ar->set_fade_out_length (new_length);
6169 /* Adjusting the xfade may affect other regions in the playlist, so we need
6170 to get undo Commands from the whole playlist rather than just the
6174 vector<Command*> cmds;
6175 ar->playlist()->rdiff (cmds);
6176 _editor->session()->add_commands (cmds);
6177 _editor->commit_reversible_command ();
6182 CrossfadeEdgeDrag::aborted (bool)
6185 // arv->redraw_start_xfade ();
6187 // arv->redraw_end_xfade ();
6191 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6192 : Drag (e, item, true)
6193 , line (new EditorCursor (*e))
6195 line->set_position (pos);
6199 RegionCutDrag::~RegionCutDrag ()
6205 RegionCutDrag::motion (GdkEvent*, bool)
6207 framepos_t where = _drags->current_pointer_frame();
6208 _editor->snap_to (where);
6210 line->set_position (where);
6214 RegionCutDrag::finished (GdkEvent*, bool)
6216 _editor->get_track_canvas()->canvas()->re_enter();
6218 framepos_t pos = _drags->current_pointer_frame();
6222 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6228 _editor->split_regions_at (pos, rs);
6232 RegionCutDrag::aborted (bool)