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 "verbose_cursor.h"
70 using namespace ARDOUR;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
81 DragManager::DragManager (Editor* e)
84 , _current_pointer_frame (0)
88 DragManager::~DragManager ()
93 /** Call abort for each active drag */
99 cerr << "Aborting drag\n";
101 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
106 if (!_drags.empty ()) {
107 _editor->set_follow_playhead (_old_follow_playhead, false);
111 _editor->abort_reversible_command();
117 DragManager::add (Drag* d)
119 d->set_manager (this);
120 _drags.push_back (d);
124 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
126 d->set_manager (this);
127 _drags.push_back (d);
132 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
134 /* Prevent follow playhead during the drag to be nice to the user */
135 _old_follow_playhead = _editor->follow_playhead ();
136 _editor->set_follow_playhead (false);
138 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
140 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
141 (*i)->start_grab (e, c);
145 /** Call end_grab for each active drag.
146 * @return true if any drag reported movement having occurred.
149 DragManager::end_grab (GdkEvent* e)
154 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
155 bool const t = (*i)->end_grab (e);
166 _editor->set_follow_playhead (_old_follow_playhead, false);
172 DragManager::mark_double_click ()
174 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
175 (*i)->set_double_click (true);
180 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
184 /* calling this implies that we expect the event to have canvas
187 * Can we guarantee that this is true?
190 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
192 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
193 bool const t = (*i)->motion_handler (e, from_autoscroll);
194 /* run all handlers; return true if at least one of them
195 returns true (indicating that the event has been handled).
207 DragManager::have_item (ArdourCanvas::Item* i) const
209 list<Drag*>::const_iterator j = _drags.begin ();
210 while (j != _drags.end() && (*j)->item () != i) {
214 return j != _drags.end ();
217 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
220 , _pointer_frame_offset (0)
221 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _initially_vertical (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
235 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
241 _cursor_ctx = CursorContext::create (*_editor, cursor);
243 _cursor_ctx->change (cursor);
250 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
252 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
254 /* we set up x/y dragging constraints on first move */
255 _x_constrained = false;
256 _y_constrained = false;
258 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
260 setup_pointer_frame_offset ();
261 _grab_frame = adjusted_frame (_raw_grab_frame, event);
262 _last_pointer_frame = _grab_frame;
263 _last_pointer_x = _grab_x;
265 if (_trackview_only) {
266 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
269 _last_pointer_y = _grab_y;
273 if (!_editor->cursors()->is_invalid (cursor)) {
274 /* CAIROCANVAS need a variant here that passes *cursor */
275 _cursor_ctx = CursorContext::create (*_editor, cursor);
278 if (_editor->session() && _editor->session()->transport_rolling()) {
281 _was_rolling = false;
284 switch (_editor->snap_type()) {
285 case SnapToRegionStart:
286 case SnapToRegionEnd:
287 case SnapToRegionSync:
288 case SnapToRegionBoundary:
289 _editor->build_region_boundary_cache ();
296 /** Call to end a drag `successfully'. Ungrabs item and calls
297 * subclass' finished() method.
299 * @param event GDK event, or 0.
300 * @return true if some movement occurred, otherwise false.
303 Drag::end_grab (GdkEvent* event)
305 _editor->stop_canvas_autoscroll ();
309 finished (event, _move_threshold_passed);
311 _editor->verbose_cursor()->hide ();
314 return _move_threshold_passed;
318 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
322 if (f > _pointer_frame_offset) {
323 pos = f - _pointer_frame_offset;
327 _editor->snap_to_with_modifier (pos, event);
334 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
336 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
340 Drag::snap_delta (guint state) const
342 if (ArdourKeyboard::indicates_snap_delta (state)) {
350 Drag::current_pointer_x() const
352 return _drags->current_pointer_x ();
356 Drag::current_pointer_y () const
358 if (!_trackview_only) {
359 return _drags->current_pointer_y ();
362 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
366 Drag::setup_snap_delta (framepos_t pos)
368 framepos_t temp = pos;
369 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
370 _snap_delta = temp - pos;
374 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
376 /* check to see if we have moved in any way that matters since the last motion event */
377 if (_move_threshold_passed &&
378 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
379 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
383 pair<framecnt_t, int> const threshold = move_threshold ();
385 bool const old_move_threshold_passed = _move_threshold_passed;
387 if (!_move_threshold_passed) {
389 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
390 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
392 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
395 if (active (_editor->mouse_mode) && _move_threshold_passed) {
397 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
399 if (old_move_threshold_passed != _move_threshold_passed) {
403 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
404 if ((event->motion.state & Gdk::BUTTON2_MASK) && Config->get_edit_mode() != Lock) {
405 _x_constrained = true;
406 _y_constrained = false;
408 _initially_vertical = true;
410 if ((event->motion.state & Gdk::BUTTON2_MASK) && Config->get_edit_mode() != Lock) {
411 _x_constrained = false;
412 _y_constrained = true;
414 _initially_vertical = false;
417 if (Config->get_edit_mode() == Lock) {
418 if (event->button.state & Gdk::BUTTON2_MASK) {
419 _x_constrained = false;
421 _x_constrained = true;
423 _y_constrained = false;
427 if (!from_autoscroll) {
428 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
431 if (!_editor->autoscroll_active() || from_autoscroll) {
434 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
436 motion (event, first_move && !_starting_point_passed);
438 if (first_move && !_starting_point_passed) {
439 _starting_point_passed = true;
442 _last_pointer_x = _drags->current_pointer_x ();
443 _last_pointer_y = current_pointer_y ();
444 _last_pointer_frame = adjusted_current_frame (event);
454 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
462 aborted (_move_threshold_passed);
464 _editor->stop_canvas_autoscroll ();
465 _editor->verbose_cursor()->hide ();
469 Drag::show_verbose_cursor_time (framepos_t frame)
471 _editor->verbose_cursor()->set_time (frame);
472 _editor->verbose_cursor()->show ();
476 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
478 _editor->verbose_cursor()->set_duration (start, end);
479 _editor->verbose_cursor()->show ();
483 Drag::show_verbose_cursor_text (string const & text)
485 _editor->verbose_cursor()->set (text);
486 _editor->verbose_cursor()->show ();
489 boost::shared_ptr<Region>
490 Drag::add_midi_region (MidiTimeAxisView* view)
492 if (_editor->session()) {
493 const TempoMap& map (_editor->session()->tempo_map());
494 framecnt_t pos = grab_frame();
495 const Meter& m = map.meter_at (pos);
496 /* not that the frame rate used here can be affected by pull up/down which
499 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
500 return view->add_region (grab_frame(), len, true);
503 return boost::shared_ptr<Region>();
506 struct EditorOrderTimeAxisViewSorter {
507 bool operator() (TimeAxisView* a, TimeAxisView* b) {
508 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
509 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
511 return ra->route()->order_key () < rb->route()->order_key ();
515 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
520 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
522 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
523 as some of the regions we are dragging may be on such tracks.
526 TrackViewList track_views = _editor->track_views;
527 track_views.sort (EditorOrderTimeAxisViewSorter ());
529 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
530 _time_axis_views.push_back (*i);
532 TimeAxisView::Children children_list = (*i)->get_child_list ();
533 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
534 _time_axis_views.push_back (j->get());
538 /* the list of views can be empty at this point if this is a region list-insert drag
541 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
542 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
545 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
549 RegionDrag::region_going_away (RegionView* v)
551 list<DraggingView>::iterator i = _views.begin ();
552 while (i != _views.end() && i->view != v) {
556 if (i != _views.end()) {
561 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
562 * or -1 if it is not found.
565 RegionDrag::find_time_axis_view (TimeAxisView* t) const
568 int const N = _time_axis_views.size ();
569 while (i < N && _time_axis_views[i] != t) {
573 if (_time_axis_views[i] != t) {
580 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
581 : RegionDrag (e, i, p, v)
584 , _last_pointer_time_axis_view (0)
585 , _last_pointer_layer (0)
590 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
594 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
596 Drag::start_grab (event, cursor);
597 setup_snap_delta (_last_frame_position);
599 show_verbose_cursor_time (_last_frame_position);
601 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
603 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
604 assert(_last_pointer_time_axis_view >= 0);
605 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
610 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
612 /* compute the amount of pointer motion in frames, and where
613 the region would be if we moved it by that much.
615 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
617 framepos_t sync_frame;
618 framecnt_t sync_offset;
621 sync_offset = _primary->region()->sync_offset (sync_dir);
623 /* we don't handle a sync point that lies before zero.
625 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
627 sync_frame = *pending_region_position + (sync_dir * sync_offset);
629 _editor->snap_to_with_modifier (sync_frame, event);
631 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
634 *pending_region_position = _last_frame_position;
637 if (*pending_region_position > max_framepos - _primary->region()->length()) {
638 *pending_region_position = _last_frame_position;
643 bool const x_move_allowed = !_x_constrained;
645 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
647 /* x movement since last time (in pixels) */
648 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
650 /* total x movement */
651 framecnt_t total_dx = *pending_region_position;
652 if (regions_came_from_canvas()) {
653 total_dx = total_dx - grab_frame ();
656 /* check that no regions have gone off the start of the session */
657 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
658 if ((i->view->region()->position() + total_dx) < 0) {
660 *pending_region_position = _last_frame_position;
671 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
677 const int tavsize = _time_axis_views.size();
678 const int dt = delta > 0 ? +1 : -1;
680 int target = start + delta - skip;
682 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
683 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
685 while (current >= 0 && current != target) {
687 if (current < 0 && dt < 0) {
690 if (current >= tavsize && dt > 0) {
693 if (current < 0 || current >= tavsize) {
697 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
698 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
702 if (distance_only && current == start + delta) {
710 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
712 if (_y_constrained) {
716 const int tavsize = _time_axis_views.size();
717 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
718 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
719 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
721 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
722 /* already in the drop zone */
723 if (delta_track >= 0) {
724 /* downward motion - OK if others are still not in the dropzone */
733 } else if (n >= tavsize) {
734 /* downward motion into drop zone. That's fine. */
738 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
739 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
740 /* not a track, or the wrong type */
744 double const l = i->layer + delta_layer;
746 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
747 mode to allow the user to place a region below another on layer 0.
749 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
750 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
751 If it has, the layers will be munged later anyway, so it's ok.
757 /* all regions being dragged are ok with this change */
761 struct DraggingViewSorter {
762 bool operator() (const DraggingView& a, const DraggingView& b) {
763 return a.time_axis_view < b.time_axis_view;
768 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
770 double delta_layer = 0;
771 int delta_time_axis_view = 0;
772 int current_pointer_time_axis_view = -1;
774 assert (!_views.empty ());
776 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
778 /* Find the TimeAxisView that the pointer is now over */
779 const double cur_y = current_pointer_y ();
780 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
781 TimeAxisView* tv = r.first;
783 if (!tv && cur_y < 0) {
784 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
788 /* find drop-zone y-position */
789 Coord last_track_bottom_edge;
790 last_track_bottom_edge = 0;
791 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
792 if (!(*t)->hidden()) {
793 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
798 if (tv && tv->view()) {
799 /* the mouse is over a track */
800 double layer = r.second;
802 if (first_move && tv->view()->layer_display() == Stacked) {
803 tv->view()->set_layer_display (Expanded);
806 /* Here's the current pointer position in terms of time axis view and layer */
807 current_pointer_time_axis_view = find_time_axis_view (tv);
808 assert(current_pointer_time_axis_view >= 0);
810 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
812 /* Work out the change in y */
814 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
815 if (!rtv || !rtv->is_track()) {
816 /* ignore busses early on. we can't move any regions on them */
817 } else if (_last_pointer_time_axis_view < 0) {
818 /* Was in the drop-zone, now over a track.
819 * Hence it must be an upward move (from the bottom)
821 * track_index is still -1, so delta must be set to
822 * move up the correct number of tracks from the bottom.
824 * This is necessary because steps may be skipped if
825 * the bottom-most track is not a valid target and/or
826 * if there are hidden tracks at the bottom.
827 * Hence the initial offset (_ddropzone) as well as the
828 * last valid pointer position (_pdropzone) need to be
829 * taken into account.
831 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
833 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
836 /* TODO needs adjustment per DraggingView,
838 * e.g. select one region on the top-layer of a track
839 * and one region which is at the bottom-layer of another track
842 * Indicated drop-zones and layering is wrong.
843 * and may infer additional layers on the target-track
844 * (depending how many layers the original track had).
846 * Or select two regions (different layers) on a same track,
847 * move across a non-layer track.. -> layering info is lost.
848 * on drop either of the regions may be on top.
850 * Proposed solution: screw it :) well,
851 * don't use delta_layer, use an absolute value
852 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
853 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
854 * 3) iterate over all DraggingView, find the one that is over the track with most layers
855 * 4) proportionally scale layer to layers available on target
857 delta_layer = current_pointer_layer - _last_pointer_layer;
860 /* for automation lanes, there is a TimeAxisView but no ->view()
861 * if (!tv) -> dropzone
863 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
864 /* Moving into the drop-zone.. */
865 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
866 /* delta_time_axis_view may not be sufficient to move into the DZ
867 * the mouse may enter it, but it may not be a valid move due to
870 * -> remember the delta needed to move into the dropzone
872 _ddropzone = delta_time_axis_view;
873 /* ..but subtract hidden tracks (or routes) at the bottom.
874 * we silently move mover them
876 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
877 - _time_axis_views.size();
879 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
880 /* move around inside the zone.
881 * This allows to move further down until all regions are in the zone.
883 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
884 assert(ptr_y >= last_track_bottom_edge);
885 assert(_ddropzone > 0);
887 /* calculate mouse position in 'tracks' below last track. */
888 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
889 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
891 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
893 delta_time_axis_view = dzpos - _pdropzone;
894 } else if (dzpos < _pdropzone && _ndropzone > 0) {
895 // move up inside the DZ
896 delta_time_axis_view = dzpos - _pdropzone;
900 /* Work out the change in x */
901 framepos_t pending_region_position;
902 double const x_delta = compute_x_delta (event, &pending_region_position);
903 _last_frame_position = pending_region_position;
905 /* calculate hidden tracks in current y-axis delta */
907 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
908 /* The mouse is more than one track below the dropzone.
909 * distance calculation is not needed (and would not work, either
910 * because the dropzone is "packed").
912 * Except when [partially] moving regions out of dropzone in a large step.
913 * (the mouse may or may not remain in the DZ)
914 * Hidden tracks at the bottom of the TAV need to be skipped.
916 * This also handles the case if the mouse entered the DZ
917 * in a large step (exessive delta), either due to fast-movement,
918 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
920 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
921 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
923 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
924 -_time_axis_views.size() - dt;
927 else if (_last_pointer_time_axis_view < 0) {
928 /* Moving out of the zone. Check for hidden tracks at the bottom. */
929 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
930 -_time_axis_views.size() - delta_time_axis_view;
932 /* calculate hidden tracks that are skipped by the pointer movement */
933 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
934 - _last_pointer_time_axis_view
935 - delta_time_axis_view;
938 /* Verify change in y */
939 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
940 /* this y movement is not allowed, so do no y movement this time */
941 delta_time_axis_view = 0;
946 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
947 /* haven't reached next snap point, and we're not switching
948 trackviews nor layers. nothing to do.
953 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
954 PlaylistDropzoneMap playlist_dropzone_map;
955 _ndropzone = 0; // number of elements currently in the dropzone
958 /* sort views by time_axis.
959 * This retains track order in the dropzone, regardless
960 * of actual selection order
962 _views.sort (DraggingViewSorter());
964 /* count number of distinct tracks of all regions
965 * being dragged, used for dropzone.
968 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
969 if (i->time_axis_view != prev_track) {
970 prev_track = i->time_axis_view;
976 _views.back().time_axis_view -
977 _views.front().time_axis_view;
979 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
980 - _views.back().time_axis_view;
982 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
986 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
988 RegionView* rv = i->view;
993 if (rv->region()->locked() || rv->region()->video_locked()) {
1000 /* reparent the regionview into a group above all
1004 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1005 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1006 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1007 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1008 /* move the item so that it continues to appear at the
1009 same location now that its parent has changed.
1011 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1014 /* If we have moved tracks, we'll fudge the layer delta so that the
1015 region gets moved back onto layer 0 on its new track; this avoids
1016 confusion when dragging regions from non-zero layers onto different
1019 double this_delta_layer = delta_layer;
1020 if (delta_time_axis_view != 0) {
1021 this_delta_layer = - i->layer;
1024 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1026 int track_index = i->time_axis_view + this_delta_time_axis_view;
1027 assert(track_index >= 0);
1029 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1030 /* Track is in the Dropzone */
1032 i->time_axis_view = track_index;
1033 assert(i->time_axis_view >= (int) _time_axis_views.size());
1036 double yposition = 0;
1037 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1038 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1041 /* store index of each new playlist as a negative count, starting at -1 */
1043 if (pdz == playlist_dropzone_map.end()) {
1044 /* compute where this new track (which doesn't exist yet) will live
1047 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1049 /* How high is this region view ? */
1051 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1052 ArdourCanvas::Rect bbox;
1055 bbox = obbox.get ();
1058 last_track_bottom_edge += bbox.height();
1060 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1063 yposition = pdz->second;
1066 /* values are zero or negative, hence the use of min() */
1067 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1072 /* The TimeAxisView that this region is now over */
1073 TimeAxisView* current_tv = _time_axis_views[track_index];
1075 /* Ensure it is moved from stacked -> expanded if appropriate */
1076 if (current_tv->view()->layer_display() == Stacked) {
1077 current_tv->view()->set_layer_display (Expanded);
1080 /* We're only allowed to go -ve in layer on Expanded views */
1081 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1082 this_delta_layer = - i->layer;
1086 rv->set_height (current_tv->view()->child_height ());
1088 /* Update show/hidden status as the region view may have come from a hidden track,
1089 or have moved to one.
1091 if (current_tv->hidden ()) {
1092 rv->get_canvas_group()->hide ();
1094 rv->get_canvas_group()->show ();
1097 /* Update the DraggingView */
1098 i->time_axis_view = track_index;
1099 i->layer += this_delta_layer;
1102 _editor->mouse_brush_insert_region (rv, pending_region_position);
1106 /* Get the y coordinate of the top of the track that this region is now over */
1107 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1109 /* And adjust for the layer that it should be on */
1110 StreamView* cv = current_tv->view ();
1111 switch (cv->layer_display ()) {
1115 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1118 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1122 /* need to get the parent of the regionview
1123 * canvas group and get its position in
1124 * equivalent coordinate space as the trackview
1125 * we are now dragging over.
1128 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1133 /* Now move the region view */
1134 rv->move (x_delta, y_delta);
1136 } /* foreach region */
1138 _total_x_delta += x_delta;
1140 if (x_delta != 0 && !_brushing) {
1141 show_verbose_cursor_time (_last_frame_position);
1144 /* keep track of pointer movement */
1146 /* the pointer is currently over a time axis view */
1148 if (_last_pointer_time_axis_view < 0) {
1149 /* last motion event was not over a time axis view
1150 * or last y-movement out of the dropzone was not valid
1153 if (delta_time_axis_view < 0) {
1154 /* in the drop zone, moving up */
1156 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1157 * We do not use negative _last_pointer_time_axis_view because
1158 * the dropzone is "packed" (the actual track offset is ignored)
1160 * As opposed to the actual number
1161 * of elements in the dropzone (_ndropzone)
1162 * _pdropzone is not constrained. This is necessary
1163 * to allow moving multiple regions with y-distance
1166 * There can be 0 elements in the dropzone,
1167 * even though the drag-pointer is inside the DZ.
1170 * [ Audio-track, Midi-track, Audio-track, DZ ]
1171 * move regions from both audio tracks at the same time into the
1172 * DZ by grabbing the region in the bottom track.
1174 assert(current_pointer_time_axis_view >= 0);
1175 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1179 /* only move out of the zone if the movement is OK */
1180 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1181 assert(delta_time_axis_view < 0);
1182 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1183 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1184 * the current position can be calculated as follows:
1186 // a well placed oofus attack can still throw this off.
1187 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1188 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1191 /* last motion event was also over a time axis view */
1192 _last_pointer_time_axis_view += delta_time_axis_view;
1193 assert(_last_pointer_time_axis_view >= 0);
1198 /* the pointer is not over a time axis view */
1199 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1200 _pdropzone += delta_time_axis_view - delta_skip;
1201 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1204 _last_pointer_layer += delta_layer;
1208 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1210 if (_copy && first_move) {
1212 if (_x_constrained) {
1213 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1215 _editor->begin_reversible_command (Operations::region_copy);
1218 /* duplicate the regionview(s) and region(s) */
1220 list<DraggingView> new_regionviews;
1222 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1224 RegionView* rv = i->view;
1225 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1226 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1228 const boost::shared_ptr<const Region> original = rv->region();
1229 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1230 region_copy->set_position (original->position());
1231 /* need to set this so that the drop zone code can work. This doesn't
1232 actually put the region into the playlist, but just sets a weak pointer
1235 region_copy->set_playlist (original->playlist());
1239 boost::shared_ptr<AudioRegion> audioregion_copy
1240 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1242 nrv = new AudioRegionView (*arv, audioregion_copy);
1244 boost::shared_ptr<MidiRegion> midiregion_copy
1245 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1246 nrv = new MidiRegionView (*mrv, midiregion_copy);
1251 nrv->get_canvas_group()->show ();
1252 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1254 /* swap _primary to the copy */
1256 if (rv == _primary) {
1260 /* ..and deselect the one we copied */
1262 rv->set_selected (false);
1265 if (!new_regionviews.empty()) {
1267 /* reflect the fact that we are dragging the copies */
1269 _views = new_regionviews;
1271 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1274 } else if (!_copy && first_move) {
1276 if (_x_constrained) {
1277 _editor->begin_reversible_command (_("fixed time region drag"));
1279 _editor->begin_reversible_command (Operations::region_drag);
1283 RegionMotionDrag::motion (event, first_move);
1287 RegionMotionDrag::finished (GdkEvent *, bool)
1289 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1290 if (!(*i)->view()) {
1294 if ((*i)->view()->layer_display() == Expanded) {
1295 (*i)->view()->set_layer_display (Stacked);
1301 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1303 RegionMotionDrag::finished (ev, movement_occurred);
1305 if (!movement_occurred) {
1309 if (was_double_click() && !_views.empty()) {
1310 DraggingView dv = _views.front();
1311 dv.view->show_region_editor ();
1318 assert (!_views.empty ());
1320 /* We might have hidden region views so that they weren't visible during the drag
1321 (when they have been reparented). Now everything can be shown again, as region
1322 views are back in their track parent groups.
1324 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1325 i->view->get_canvas_group()->show ();
1328 bool const changed_position = (_last_frame_position != _primary->region()->position());
1329 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1330 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1350 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1354 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1356 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1361 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1362 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1363 uint32_t output_chan = region->n_channels();
1364 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1365 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1367 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1368 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1370 rtav->set_height (original->current_height());
1374 ChanCount one_midi_port (DataType::MIDI, 1);
1375 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1376 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1377 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1379 rtav->set_height (original->current_height());
1384 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1390 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1392 RegionSelection new_views;
1393 PlaylistSet modified_playlists;
1394 RouteTimeAxisView* new_time_axis_view = 0;
1397 /* all changes were made during motion event handlers */
1399 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1403 _editor->commit_reversible_command ();
1407 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1408 PlaylistMapping playlist_mapping;
1410 /* insert the regions into their new playlists */
1411 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1413 RouteTimeAxisView* dest_rtv = 0;
1415 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1421 if (changed_position && !_x_constrained) {
1422 where = i->view->region()->position() - drag_delta;
1424 where = i->view->region()->position();
1427 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1428 /* dragged to drop zone */
1430 PlaylistMapping::iterator pm;
1432 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1433 /* first region from this original playlist: create a new track */
1434 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1435 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1436 dest_rtv = new_time_axis_view;
1438 /* we already created a new track for regions from this playlist, use it */
1439 dest_rtv = pm->second;
1442 /* destination time axis view is the one we dragged to */
1443 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1446 if (dest_rtv != 0) {
1447 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1448 if (new_view != 0) {
1449 new_views.push_back (new_view);
1453 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1454 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1457 list<DraggingView>::const_iterator next = i;
1463 /* If we've created new regions either by copying or moving
1464 to a new track, we want to replace the old selection with the new ones
1467 if (new_views.size() > 0) {
1468 _editor->selection->set (new_views);
1471 /* write commands for the accumulated diffs for all our modified playlists */
1472 add_stateful_diff_commands_for_playlists (modified_playlists);
1474 _editor->commit_reversible_command ();
1478 RegionMoveDrag::finished_no_copy (
1479 bool const changed_position,
1480 bool const changed_tracks,
1481 framecnt_t const drag_delta
1484 RegionSelection new_views;
1485 PlaylistSet modified_playlists;
1486 PlaylistSet frozen_playlists;
1487 set<RouteTimeAxisView*> views_to_update;
1488 RouteTimeAxisView* new_time_axis_view = 0;
1491 /* all changes were made during motion event handlers */
1492 _editor->commit_reversible_command ();
1496 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1497 PlaylistMapping playlist_mapping;
1499 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1501 RegionView* rv = i->view;
1502 RouteTimeAxisView* dest_rtv = 0;
1504 if (rv->region()->locked() || rv->region()->video_locked()) {
1509 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1510 /* dragged to drop zone */
1512 PlaylistMapping::iterator pm;
1514 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1515 /* first region from this original playlist: create a new track */
1516 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1517 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1518 dest_rtv = new_time_axis_view;
1520 /* we already created a new track for regions from this playlist, use it */
1521 dest_rtv = pm->second;
1525 /* destination time axis view is the one we dragged to */
1526 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1531 double const dest_layer = i->layer;
1533 views_to_update.insert (dest_rtv);
1537 if (changed_position && !_x_constrained) {
1538 where = rv->region()->position() - drag_delta;
1540 where = rv->region()->position();
1543 if (changed_tracks) {
1545 /* insert into new playlist */
1547 RegionView* new_view = insert_region_into_playlist (
1548 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1551 if (new_view == 0) {
1556 new_views.push_back (new_view);
1558 /* remove from old playlist */
1560 /* the region that used to be in the old playlist is not
1561 moved to the new one - we use a copy of it. as a result,
1562 any existing editor for the region should no longer be
1565 rv->hide_region_editor();
1568 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1572 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1574 /* this movement may result in a crossfade being modified, or a layering change,
1575 so we need to get undo data from the playlist as well as the region.
1578 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1580 playlist->clear_changes ();
1583 rv->region()->clear_changes ();
1586 motion on the same track. plonk the previously reparented region
1587 back to its original canvas group (its streamview).
1588 No need to do anything for copies as they are fake regions which will be deleted.
1591 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1592 rv->get_canvas_group()->set_y_position (i->initial_y);
1595 /* just change the model */
1596 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1597 playlist->set_layer (rv->region(), dest_layer);
1600 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1602 r = frozen_playlists.insert (playlist);
1605 playlist->freeze ();
1608 rv->region()->set_position (where);
1610 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1613 if (changed_tracks) {
1615 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1616 was selected in all of them, then removing it from a playlist will have removed all
1617 trace of it from _views (i.e. there were N regions selected, we removed 1,
1618 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1619 corresponding regionview, and _views is now empty).
1621 This could have invalidated any and all iterators into _views.
1623 The heuristic we use here is: if the region selection is empty, break out of the loop
1624 here. if the region selection is not empty, then restart the loop because we know that
1625 we must have removed at least the region(view) we've just been working on as well as any
1626 that we processed on previous iterations.
1628 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1629 we can just iterate.
1633 if (_views.empty()) {
1644 /* If we've created new regions either by copying or moving
1645 to a new track, we want to replace the old selection with the new ones
1648 if (new_views.size() > 0) {
1649 _editor->selection->set (new_views);
1652 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1656 /* write commands for the accumulated diffs for all our modified playlists */
1657 add_stateful_diff_commands_for_playlists (modified_playlists);
1659 _editor->commit_reversible_command ();
1661 /* We have futzed with the layering of canvas items on our streamviews.
1662 If any region changed layer, this will have resulted in the stream
1663 views being asked to set up their region views, and all will be well.
1664 If not, we might now have badly-ordered region views. Ask the StreamViews
1665 involved to sort themselves out, just in case.
1668 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1669 (*i)->view()->playlist_layered ((*i)->track ());
1673 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1674 * @param region Region to remove.
1675 * @param playlist playlist To remove from.
1676 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1677 * that clear_changes () is only called once per playlist.
1680 RegionMoveDrag::remove_region_from_playlist (
1681 boost::shared_ptr<Region> region,
1682 boost::shared_ptr<Playlist> playlist,
1683 PlaylistSet& modified_playlists
1686 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1689 playlist->clear_changes ();
1692 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1696 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1697 * clearing the playlist's diff history first if necessary.
1698 * @param region Region to insert.
1699 * @param dest_rtv Destination RouteTimeAxisView.
1700 * @param dest_layer Destination layer.
1701 * @param where Destination position.
1702 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1703 * that clear_changes () is only called once per playlist.
1704 * @return New RegionView, or 0 if no insert was performed.
1707 RegionMoveDrag::insert_region_into_playlist (
1708 boost::shared_ptr<Region> region,
1709 RouteTimeAxisView* dest_rtv,
1712 PlaylistSet& modified_playlists
1715 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1716 if (!dest_playlist) {
1720 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1721 _new_region_view = 0;
1722 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1724 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1725 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1727 dest_playlist->clear_changes ();
1730 dest_playlist->add_region (region, where);
1732 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1733 dest_playlist->set_layer (region, dest_layer);
1738 assert (_new_region_view);
1740 return _new_region_view;
1744 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1746 _new_region_view = rv;
1750 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1752 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1753 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1755 _editor->session()->add_command (c);
1764 RegionMoveDrag::aborted (bool movement_occurred)
1768 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1769 list<DraggingView>::const_iterator next = i;
1778 RegionMotionDrag::aborted (movement_occurred);
1783 RegionMotionDrag::aborted (bool)
1785 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1787 StreamView* sview = (*i)->view();
1790 if (sview->layer_display() == Expanded) {
1791 sview->set_layer_display (Stacked);
1796 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1797 RegionView* rv = i->view;
1798 TimeAxisView* tv = &(rv->get_time_axis_view ());
1799 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1801 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1802 rv->get_canvas_group()->set_y_position (0);
1804 rv->move (-_total_x_delta, 0);
1805 rv->set_height (rtv->view()->child_height ());
1809 /** @param b true to brush, otherwise false.
1810 * @param c true to make copies of the regions being moved, otherwise false.
1812 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1813 : RegionMotionDrag (e, i, p, v, b)
1816 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1819 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1820 if (rtv && rtv->is_track()) {
1821 speed = rtv->track()->speed ();
1824 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1828 RegionMoveDrag::setup_pointer_frame_offset ()
1830 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1833 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1834 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1836 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1838 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1839 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1841 _primary = v->view()->create_region_view (r, false, false);
1843 _primary->get_canvas_group()->show ();
1844 _primary->set_position (pos, 0);
1845 _views.push_back (DraggingView (_primary, this, v));
1847 _last_frame_position = pos;
1849 _item = _primary->get_canvas_group ();
1853 RegionInsertDrag::finished (GdkEvent *, bool)
1855 int pos = _views.front().time_axis_view;
1856 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1858 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1860 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1861 _primary->get_canvas_group()->set_y_position (0);
1863 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1865 _editor->begin_reversible_command (Operations::insert_region);
1866 playlist->clear_changes ();
1867 playlist->add_region (_primary->region (), _last_frame_position);
1869 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1870 if (Config->get_edit_mode() == Ripple) {
1871 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1874 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1875 _editor->commit_reversible_command ();
1883 RegionInsertDrag::aborted (bool)
1890 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1891 : RegionMoveDrag (e, i, p, v, false, false)
1893 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1896 struct RegionSelectionByPosition {
1897 bool operator() (RegionView*a, RegionView* b) {
1898 return a->region()->position () < b->region()->position();
1903 RegionSpliceDrag::motion (GdkEvent* event, bool)
1905 /* Which trackview is this ? */
1907 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1908 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1910 /* The region motion is only processed if the pointer is over
1914 if (!tv || !tv->is_track()) {
1915 /* To make sure we hide the verbose canvas cursor when the mouse is
1916 not held over an audio track.
1918 _editor->verbose_cursor()->hide ();
1921 _editor->verbose_cursor()->show ();
1926 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1932 RegionSelection copy;
1933 _editor->selection->regions.by_position(copy);
1935 framepos_t const pf = adjusted_current_frame (event);
1937 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1939 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1945 boost::shared_ptr<Playlist> playlist;
1947 if ((playlist = atv->playlist()) == 0) {
1951 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1956 if (pf < (*i)->region()->last_frame() + 1) {
1960 if (pf > (*i)->region()->first_frame()) {
1966 playlist->shuffle ((*i)->region(), dir);
1971 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1973 RegionMoveDrag::finished (event, movement_occurred);
1977 RegionSpliceDrag::aborted (bool)
1987 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1990 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1992 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1993 RegionSelection to_ripple;
1994 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1995 if ((*i)->position() >= where) {
1996 to_ripple.push_back (rtv->view()->find_view(*i));
2000 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2001 if (!exclude.contains (*i)) {
2002 // the selection has already been added to _views
2004 if (drag_in_progress) {
2005 // do the same things that RegionMotionDrag::motion does when
2006 // first_move is true, for the region views that we're adding
2007 // to _views this time
2010 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2011 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2012 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2013 rvg->reparent (_editor->_drag_motion_group);
2015 // we only need to move in the y direction
2016 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2021 _views.push_back (DraggingView (*i, this, tav));
2027 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2030 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2031 // we added all the regions after the selection
2033 std::list<DraggingView>::iterator to_erase = i++;
2034 if (!_editor->selection->regions.contains (to_erase->view)) {
2035 // restore the non-selected regions to their original playlist & positions,
2036 // and then ripple them back by the length of the regions that were dragged away
2037 // do the same things as RegionMotionDrag::aborted
2039 RegionView *rv = to_erase->view;
2040 TimeAxisView* tv = &(rv->get_time_axis_view ());
2041 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2044 // plonk them back onto their own track
2045 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2046 rv->get_canvas_group()->set_y_position (0);
2050 // move the underlying region to match the view
2051 rv->region()->set_position (rv->region()->position() + amount);
2053 // restore the view to match the underlying region's original position
2054 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2057 rv->set_height (rtv->view()->child_height ());
2058 _views.erase (to_erase);
2064 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2066 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2068 return allow_moves_across_tracks;
2076 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2077 : RegionMoveDrag (e, i, p, v, false, false)
2079 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2080 // compute length of selection
2081 RegionSelection selected_regions = _editor->selection->regions;
2082 selection_length = selected_regions.end_frame() - selected_regions.start();
2084 // we'll only allow dragging to another track in ripple mode if all the regions
2085 // being dragged start off on the same track
2086 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2089 exclude = new RegionList;
2090 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2091 exclude->push_back((*i)->region());
2094 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2095 RegionSelection copy;
2096 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2098 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2099 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2101 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2102 // find ripple start point on each applicable playlist
2103 RegionView *first_selected_on_this_track = NULL;
2104 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2105 if ((*i)->region()->playlist() == (*pi)) {
2106 // region is on this playlist - it's the first, because they're sorted
2107 first_selected_on_this_track = *i;
2111 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2112 add_all_after_to_views (
2113 &first_selected_on_this_track->get_time_axis_view(),
2114 first_selected_on_this_track->region()->position(),
2115 selected_regions, false);
2118 if (allow_moves_across_tracks) {
2119 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2127 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2129 /* Which trackview is this ? */
2131 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2132 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2134 /* The region motion is only processed if the pointer is over
2138 if (!tv || !tv->is_track()) {
2139 /* To make sure we hide the verbose canvas cursor when the mouse is
2140 not held over an audiotrack.
2142 _editor->verbose_cursor()->hide ();
2146 framepos_t where = adjusted_current_frame (event);
2147 assert (where >= 0);
2149 double delta = compute_x_delta (event, &after);
2151 framecnt_t amount = _editor->pixel_to_sample (delta);
2153 if (allow_moves_across_tracks) {
2154 // all the originally selected regions were on the same track
2156 framecnt_t adjust = 0;
2157 if (prev_tav && tv != prev_tav) {
2158 // dragged onto a different track
2159 // remove the unselected regions from _views, restore them to their original positions
2160 // and add the regions after the drop point on the new playlist to _views instead.
2161 // undo the effect of rippling the previous playlist, and include the effect of removing
2162 // the dragged region(s) from this track
2164 remove_unselected_from_views (prev_amount, false);
2165 // ripple previous playlist according to the regions that have been removed onto the new playlist
2166 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2169 // move just the selected regions
2170 RegionMoveDrag::motion(event, first_move);
2172 // ensure that the ripple operation on the new playlist inserts selection_length time
2173 adjust = selection_length;
2174 // ripple the new current playlist
2175 tv->playlist()->ripple (where, amount+adjust, exclude);
2177 // add regions after point where drag entered this track to subsequent ripples
2178 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2181 // motion on same track
2182 RegionMoveDrag::motion(event, first_move);
2186 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2187 prev_position = where;
2189 // selection encompasses multiple tracks - just drag
2190 // cross-track drags are forbidden
2191 RegionMoveDrag::motion(event, first_move);
2194 if (!_x_constrained) {
2195 prev_amount += amount;
2198 _last_frame_position = after;
2202 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2204 if (!movement_occurred) {
2208 if (was_double_click() && !_views.empty()) {
2209 DraggingView dv = _views.front();
2210 dv.view->show_region_editor ();
2217 _editor->begin_reversible_command(_("Ripple drag"));
2219 // remove the regions being rippled from the dragging view, updating them to
2220 // their new positions
2221 remove_unselected_from_views (prev_amount, true);
2223 if (allow_moves_across_tracks) {
2225 // if regions were dragged across tracks, we've rippled any later
2226 // regions on the track the regions were dragged off, so we need
2227 // to add the original track to the undo record
2228 orig_tav->playlist()->clear_changes();
2229 vector<Command*> cmds;
2230 orig_tav->playlist()->rdiff (cmds);
2231 _editor->session()->add_commands (cmds);
2233 if (prev_tav && prev_tav != orig_tav) {
2234 prev_tav->playlist()->clear_changes();
2235 vector<Command*> cmds;
2236 prev_tav->playlist()->rdiff (cmds);
2237 _editor->session()->add_commands (cmds);
2240 // selection spanned multiple tracks - all will need adding to undo record
2242 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2243 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2245 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2246 (*pi)->clear_changes();
2247 vector<Command*> cmds;
2248 (*pi)->rdiff (cmds);
2249 _editor->session()->add_commands (cmds);
2253 // other modified playlists are added to undo by RegionMoveDrag::finished()
2254 RegionMoveDrag::finished (event, movement_occurred);
2255 _editor->commit_reversible_command();
2259 RegionRippleDrag::aborted (bool movement_occurred)
2261 RegionMoveDrag::aborted (movement_occurred);
2266 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2268 _view (dynamic_cast<MidiTimeAxisView*> (v))
2270 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2276 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2279 _region = add_midi_region (_view);
2280 _view->playlist()->freeze ();
2283 framepos_t const f = adjusted_current_frame (event);
2284 if (f < grab_frame()) {
2285 _region->set_position (f);
2288 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2289 so that if this region is duplicated, its duplicate starts on
2290 a snap point rather than 1 frame after a snap point. Otherwise things get
2291 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2292 place snapped notes at the start of the region.
2295 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2296 _region->set_length (len < 1 ? 1 : len);
2302 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2304 if (!movement_occurred) {
2305 add_midi_region (_view);
2307 _view->playlist()->thaw ();
2312 RegionCreateDrag::aborted (bool)
2315 _view->playlist()->thaw ();
2321 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2326 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2330 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2332 Gdk::Cursor* cursor;
2333 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2335 float x_fraction = cnote->mouse_x_fraction ();
2337 if (x_fraction > 0.0 && x_fraction < 0.25) {
2338 cursor = _editor->cursors()->left_side_trim;
2341 cursor = _editor->cursors()->right_side_trim;
2345 Drag::start_grab (event, cursor);
2347 region = &cnote->region_view();
2350 temp = region->snap_to_pixel (cnote->x0 (), true);
2351 _snap_delta = temp - cnote->x0 ();
2355 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2361 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2363 if (ms.size() > 1) {
2364 /* has to be relative, may make no sense otherwise */
2368 /* select this note; if it is already selected, preserve the existing selection,
2369 otherwise make this note the only one selected.
2371 region->note_selected (cnote, cnote->selected ());
2373 _editor->begin_reversible_command (_("resize notes"));
2375 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2376 MidiRegionSelection::iterator next;
2379 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2381 mrv->begin_resizing (at_front);
2388 NoteResizeDrag::motion (GdkEvent* event, bool /*first_move*/)
2390 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2391 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2392 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2394 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2398 bool apply_snap_delta = !ArdourKeyboard::indicates_snap_delta (event->button.state);
2400 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2401 if (_editor->snap_mode () != SnapOff) {
2405 if (_editor->snap_mode () == SnapOff) {
2407 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2408 if (!apply_snap_delta) {
2414 if (apply_snap_delta) {
2418 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2424 NoteResizeDrag::finished (GdkEvent* event, bool /*movement_occurred*/)
2426 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2427 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2428 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2430 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2433 bool apply_snap_delta = !ArdourKeyboard::indicates_snap_delta (event->button.state);
2435 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2436 if (_editor->snap_mode () != SnapOff) {
2440 if (_editor->snap_mode () == SnapOff) {
2442 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2443 if (!apply_snap_delta) {
2448 if (apply_snap_delta) {
2451 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2455 _editor->commit_reversible_command ();
2459 NoteResizeDrag::aborted (bool)
2461 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2462 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2463 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2465 mrv->abort_resizing ();
2470 AVDraggingView::AVDraggingView (RegionView* v)
2473 initial_position = v->region()->position ();
2476 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2479 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2482 TrackViewList empty;
2484 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2485 std::list<RegionView*> views = rs.by_layer();
2487 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2488 RegionView* rv = (*i);
2489 if (!rv->region()->video_locked()) {
2492 _views.push_back (AVDraggingView (rv));
2497 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2499 Drag::start_grab (event);
2500 if (_editor->session() == 0) {
2504 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2505 _max_backwards_drag = (
2506 ARDOUR_UI::instance()->video_timeline->get_duration()
2507 + ARDOUR_UI::instance()->video_timeline->get_offset()
2508 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2511 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2512 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2513 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2516 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2519 Timecode::Time timecode;
2520 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2521 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);
2522 show_verbose_cursor_text (buf);
2526 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2528 if (_editor->session() == 0) {
2531 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2535 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2536 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2538 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2539 dt = - _max_backwards_drag;
2542 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2543 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2545 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2546 RegionView* rv = i->view;
2547 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2550 rv->region()->clear_changes ();
2551 rv->region()->suspend_property_changes();
2553 rv->region()->set_position(i->initial_position + dt);
2554 rv->region_changed(ARDOUR::Properties::position);
2557 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2558 Timecode::Time timecode;
2559 Timecode::Time timediff;
2561 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2562 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2563 snprintf (buf, sizeof (buf),
2564 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2565 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2566 , _("Video Start:"),
2567 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2569 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2571 show_verbose_cursor_text (buf);
2575 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2577 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2581 if (!movement_occurred || ! _editor->session()) {
2585 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2587 _editor->begin_reversible_command (_("Move Video"));
2589 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2590 ARDOUR_UI::instance()->video_timeline->save_undo();
2591 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2592 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2594 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2595 i->view->drag_end();
2596 i->view->region()->resume_property_changes ();
2598 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2601 _editor->session()->maybe_update_session_range(
2602 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2603 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2607 _editor->commit_reversible_command ();
2611 VideoTimeLineDrag::aborted (bool)
2613 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2616 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2617 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2619 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2620 i->view->region()->resume_property_changes ();
2621 i->view->region()->set_position(i->initial_position);
2625 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2626 : RegionDrag (e, i, p, v)
2627 , _preserve_fade_anchor (preserve_fade_anchor)
2628 , _jump_position_when_done (false)
2630 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2634 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2637 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2638 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2640 if (tv && tv->is_track()) {
2641 speed = tv->track()->speed();
2644 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2645 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2646 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2648 framepos_t const pf = adjusted_current_frame (event);
2649 setup_snap_delta (region_start);
2651 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2652 /* Move the contents of the region around without changing the region bounds */
2653 _operation = ContentsTrim;
2654 Drag::start_grab (event, _editor->cursors()->trimmer);
2656 /* These will get overridden for a point trim.*/
2657 if (pf < (region_start + region_length/2)) {
2658 /* closer to front */
2659 _operation = StartTrim;
2660 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2661 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2663 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2667 _operation = EndTrim;
2668 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2669 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2671 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2675 /* jump trim disabled for now
2676 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2677 _jump_position_when_done = true;
2681 switch (_operation) {
2683 show_verbose_cursor_time (region_start);
2684 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2685 i->view->trim_front_starting ();
2689 show_verbose_cursor_duration (region_start, region_end);
2692 show_verbose_cursor_time (pf);
2696 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2697 i->view->region()->suspend_property_changes ();
2702 TrimDrag::motion (GdkEvent* event, bool first_move)
2704 RegionView* rv = _primary;
2707 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2708 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2709 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2710 frameoffset_t frame_delta = 0;
2712 if (tv && tv->is_track()) {
2713 speed = tv->track()->speed();
2715 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2716 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2722 switch (_operation) {
2724 trim_type = "Region start trim";
2727 trim_type = "Region end trim";
2730 trim_type = "Region content trim";
2737 _editor->begin_reversible_command (trim_type);
2739 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2740 RegionView* rv = i->view;
2741 rv->enable_display (false);
2742 rv->region()->playlist()->clear_owned_changes ();
2744 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2747 arv->temporarily_hide_envelope ();
2751 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2752 insert_result = _editor->motion_frozen_playlists.insert (pl);
2754 if (insert_result.second) {
2760 bool non_overlap_trim = false;
2762 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2763 non_overlap_trim = true;
2766 /* contstrain trim to fade length */
2767 if (_preserve_fade_anchor) {
2768 switch (_operation) {
2770 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2771 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2773 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2774 if (ar->locked()) continue;
2775 framecnt_t len = ar->fade_in()->back()->when;
2776 if (len < dt) dt = min(dt, len);
2780 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2781 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2783 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2784 if (ar->locked()) continue;
2785 framecnt_t len = ar->fade_out()->back()->when;
2786 if (len < -dt) dt = max(dt, -len);
2795 switch (_operation) {
2797 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2798 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2799 if (changed && _preserve_fade_anchor) {
2800 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2802 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2803 framecnt_t len = ar->fade_in()->back()->when;
2804 framecnt_t diff = ar->first_frame() - i->initial_position;
2805 framepos_t new_length = len - diff;
2806 i->anchored_fade_length = min (ar->length(), new_length);
2807 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2808 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2815 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2816 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2817 if (changed && _preserve_fade_anchor) {
2818 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2820 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2821 framecnt_t len = ar->fade_out()->back()->when;
2822 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2823 framepos_t new_length = len + diff;
2824 i->anchored_fade_length = min (ar->length(), new_length);
2825 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2826 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2834 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2836 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2837 i->view->move_contents (frame_delta);
2843 switch (_operation) {
2845 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2848 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2851 // show_verbose_cursor_time (frame_delta);
2857 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2859 if (movement_occurred) {
2860 motion (event, false);
2862 if (_operation == StartTrim) {
2863 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2865 /* This must happen before the region's StatefulDiffCommand is created, as it may
2866 `correct' (ahem) the region's _start from being negative to being zero. It
2867 needs to be zero in the undo record.
2869 i->view->trim_front_ending ();
2871 if (_preserve_fade_anchor) {
2872 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2874 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2875 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2876 ar->set_fade_in_length(i->anchored_fade_length);
2877 ar->set_fade_in_active(true);
2880 if (_jump_position_when_done) {
2881 i->view->region()->set_position (i->initial_position);
2884 } else if (_operation == EndTrim) {
2885 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2886 if (_preserve_fade_anchor) {
2887 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2889 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2890 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2891 ar->set_fade_out_length(i->anchored_fade_length);
2892 ar->set_fade_out_active(true);
2895 if (_jump_position_when_done) {
2896 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2901 if (!_views.empty()) {
2902 if (_operation == StartTrim) {
2903 _editor->maybe_locate_with_edit_preroll(
2904 _views.begin()->view->region()->position());
2906 if (_operation == EndTrim) {
2907 _editor->maybe_locate_with_edit_preroll(
2908 _views.begin()->view->region()->position() +
2909 _views.begin()->view->region()->length());
2913 if (!_editor->selection->selected (_primary)) {
2914 _primary->thaw_after_trim ();
2917 set<boost::shared_ptr<Playlist> > diffed_playlists;
2919 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2920 i->view->thaw_after_trim ();
2921 i->view->enable_display (true);
2923 /* Trimming one region may affect others on the playlist, so we need
2924 to get undo Commands from the whole playlist rather than just the
2925 region. Use diffed_playlists to make sure we don't diff a given
2926 playlist more than once.
2928 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2929 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2930 vector<Command*> cmds;
2932 _editor->session()->add_commands (cmds);
2933 diffed_playlists.insert (p);
2938 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2942 _editor->motion_frozen_playlists.clear ();
2943 _editor->commit_reversible_command();
2946 /* no mouse movement */
2947 _editor->point_trim (event, adjusted_current_frame (event));
2950 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2951 if (_operation == StartTrim) {
2952 i->view->trim_front_ending ();
2955 i->view->region()->resume_property_changes ();
2960 TrimDrag::aborted (bool movement_occurred)
2962 /* Our motion method is changing model state, so use the Undo system
2963 to cancel. Perhaps not ideal, as this will leave an Undo point
2964 behind which may be slightly odd from the user's point of view.
2969 if (movement_occurred) {
2973 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2974 i->view->region()->resume_property_changes ();
2979 TrimDrag::setup_pointer_frame_offset ()
2981 list<DraggingView>::iterator i = _views.begin ();
2982 while (i != _views.end() && i->view != _primary) {
2986 if (i == _views.end()) {
2990 switch (_operation) {
2992 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2995 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3002 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3006 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3007 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3012 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3014 Drag::start_grab (event, cursor);
3015 show_verbose_cursor_time (adjusted_current_frame(event));
3019 MeterMarkerDrag::setup_pointer_frame_offset ()
3021 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3025 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3027 if (!_marker->meter().movable()) {
3033 // create a dummy marker for visual representation of moving the
3034 // section, because whether its a copy or not, we're going to
3035 // leave or lose the original marker (leave if its a copy; lose if its
3036 // not, because we'll remove it from the map).
3038 MeterSection section (_marker->meter());
3040 if (!section.movable()) {
3045 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3047 _marker = new MeterMarker (
3049 *_editor->meter_group,
3050 ARDOUR_UI::config()->color ("meter marker"),
3052 *new MeterSection (_marker->meter())
3055 /* use the new marker for the grab */
3056 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3059 TempoMap& map (_editor->session()->tempo_map());
3060 /* get current state */
3061 before_state = &map.get_state();
3062 /* remove the section while we drag it */
3063 map.remove_meter (section, true);
3067 framepos_t const pf = adjusted_current_frame (event);
3069 _marker->set_position (pf);
3070 show_verbose_cursor_time (pf);
3074 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3076 if (!movement_occurred) {
3077 if (was_double_click()) {
3078 _editor->edit_meter_marker (*_marker);
3083 if (!_marker->meter().movable()) {
3087 motion (event, false);
3089 Timecode::BBT_Time when;
3091 TempoMap& map (_editor->session()->tempo_map());
3092 map.bbt_time (last_pointer_frame(), when);
3094 if (_copy == true) {
3095 _editor->begin_reversible_command (_("copy meter mark"));
3096 XMLNode &before = map.get_state();
3097 map.add_meter (_marker->meter(), when);
3098 XMLNode &after = map.get_state();
3099 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3100 _editor->commit_reversible_command ();
3103 _editor->begin_reversible_command (_("move meter mark"));
3105 /* we removed it before, so add it back now */
3107 map.add_meter (_marker->meter(), when);
3108 XMLNode &after = map.get_state();
3109 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3110 _editor->commit_reversible_command ();
3113 // delete the dummy marker we used for visual representation while moving.
3114 // a new visual marker will show up automatically.
3119 MeterMarkerDrag::aborted (bool moved)
3121 _marker->set_position (_marker->meter().frame ());
3124 TempoMap& map (_editor->session()->tempo_map());
3125 /* we removed it before, so add it back now */
3126 map.add_meter (_marker->meter(), _marker->meter().frame());
3127 // delete the dummy marker we used for visual representation while moving.
3128 // a new visual marker will show up automatically.
3133 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3137 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3139 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3144 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3146 Drag::start_grab (event, cursor);
3147 show_verbose_cursor_time (adjusted_current_frame (event));
3151 TempoMarkerDrag::setup_pointer_frame_offset ()
3153 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3157 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3159 if (!_marker->tempo().movable()) {
3165 // create a dummy marker for visual representation of moving the
3166 // section, because whether its a copy or not, we're going to
3167 // leave or lose the original marker (leave if its a copy; lose if its
3168 // not, because we'll remove it from the map).
3170 // create a dummy marker for visual representation of moving the copy.
3171 // The actual copying is not done before we reach the finish callback.
3174 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3176 TempoSection section (_marker->tempo());
3178 _marker = new TempoMarker (
3180 *_editor->tempo_group,
3181 ARDOUR_UI::config()->color ("tempo marker"),
3183 *new TempoSection (_marker->tempo())
3186 /* use the new marker for the grab */
3187 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3190 TempoMap& map (_editor->session()->tempo_map());
3191 /* get current state */
3192 before_state = &map.get_state();
3193 /* remove the section while we drag it */
3194 map.remove_tempo (section, true);
3198 framepos_t const pf = adjusted_current_frame (event);
3199 _marker->set_position (pf);
3200 show_verbose_cursor_time (pf);
3204 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3206 if (!movement_occurred) {
3207 if (was_double_click()) {
3208 _editor->edit_tempo_marker (*_marker);
3213 if (!_marker->tempo().movable()) {
3217 motion (event, false);
3219 TempoMap& map (_editor->session()->tempo_map());
3220 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3221 Timecode::BBT_Time when;
3223 map.bbt_time (beat_time, when);
3225 if (_copy == true) {
3226 _editor->begin_reversible_command (_("copy tempo mark"));
3227 XMLNode &before = map.get_state();
3228 map.add_tempo (_marker->tempo(), when);
3229 XMLNode &after = map.get_state();
3230 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3231 _editor->commit_reversible_command ();
3234 _editor->begin_reversible_command (_("move tempo mark"));
3235 /* we removed it before, so add it back now */
3236 map.add_tempo (_marker->tempo(), when);
3237 XMLNode &after = map.get_state();
3238 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3239 _editor->commit_reversible_command ();
3242 // delete the dummy marker we used for visual representation while moving.
3243 // a new visual marker will show up automatically.
3248 TempoMarkerDrag::aborted (bool moved)
3250 _marker->set_position (_marker->tempo().frame());
3252 TempoMap& map (_editor->session()->tempo_map());
3253 /* we removed it before, so add it back now */
3254 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3255 // delete the dummy marker we used for visual representation while moving.
3256 // a new visual marker will show up automatically.
3261 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3262 : Drag (e, &c.track_canvas_item(), false)
3266 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3269 /** Do all the things we do when dragging the playhead to make it look as though
3270 * we have located, without actually doing the locate (because that would cause
3271 * the diskstream buffers to be refilled, which is too slow).
3274 CursorDrag::fake_locate (framepos_t t)
3276 _editor->playhead_cursor->set_position (t);
3278 Session* s = _editor->session ();
3279 if (s->timecode_transmission_suspended ()) {
3280 framepos_t const f = _editor->playhead_cursor->current_frame ();
3281 /* This is asynchronous so it will be sent "now"
3283 s->send_mmc_locate (f);
3284 /* These are synchronous and will be sent during the next
3287 s->queue_full_time_code ();
3288 s->queue_song_position_pointer ();
3291 show_verbose_cursor_time (t);
3292 _editor->UpdateAllTransportClocks (t);
3296 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3298 Drag::start_grab (event, c);
3299 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3301 _grab_zoom = _editor->samples_per_pixel;
3303 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3305 _editor->snap_to_with_modifier (where, event);
3307 _editor->_dragging_playhead = true;
3309 Session* s = _editor->session ();
3311 /* grab the track canvas item as well */
3313 _cursor.track_canvas_item().grab();
3316 if (_was_rolling && _stop) {
3320 if (s->is_auditioning()) {
3321 s->cancel_audition ();
3325 if (AudioEngine::instance()->connected()) {
3327 /* do this only if we're the engine is connected
3328 * because otherwise this request will never be
3329 * serviced and we'll busy wait forever. likewise,
3330 * notice if we are disconnected while waiting for the
3331 * request to be serviced.
3334 s->request_suspend_timecode_transmission ();
3335 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3336 /* twiddle our thumbs */
3341 fake_locate (where - snap_delta (event->button.state));
3345 CursorDrag::motion (GdkEvent* event, bool)
3347 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3348 _editor->snap_to_with_modifier (where, event);
3349 if (where != last_pointer_frame()) {
3350 fake_locate (where - snap_delta (event->button.state));
3355 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3357 _editor->_dragging_playhead = false;
3359 _cursor.track_canvas_item().ungrab();
3361 if (!movement_occurred && _stop) {
3365 motion (event, false);
3367 Session* s = _editor->session ();
3369 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3370 _editor->_pending_locate_request = true;
3371 s->request_resume_timecode_transmission ();
3376 CursorDrag::aborted (bool)
3378 _cursor.track_canvas_item().ungrab();
3380 if (_editor->_dragging_playhead) {
3381 _editor->session()->request_resume_timecode_transmission ();
3382 _editor->_dragging_playhead = false;
3385 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3388 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3389 : RegionDrag (e, i, p, v)
3391 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3395 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3397 Drag::start_grab (event, cursor);
3399 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3400 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3401 setup_snap_delta (r->position ());
3403 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3407 FadeInDrag::setup_pointer_frame_offset ()
3409 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3410 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3411 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3415 FadeInDrag::motion (GdkEvent* event, bool)
3417 framecnt_t fade_length;
3419 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3420 _editor->snap_to_with_modifier (pos, event);
3421 pos -= snap_delta (event->button.state);
3423 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3425 if (pos < (region->position() + 64)) {
3426 fade_length = 64; // this should be a minimum defined somewhere
3427 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3428 fade_length = region->length() - region->fade_out()->back()->when - 1;
3430 fade_length = pos - region->position();
3433 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3435 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3441 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3444 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3448 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3450 if (!movement_occurred) {
3454 framecnt_t fade_length;
3455 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3456 _editor->snap_to_with_modifier (pos, event);
3457 pos -= snap_delta (event->button.state);
3459 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3461 if (pos < (region->position() + 64)) {
3462 fade_length = 64; // this should be a minimum defined somewhere
3463 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3464 fade_length = region->length() - region->fade_out()->back()->when - 1;
3466 fade_length = pos - region->position();
3469 _editor->begin_reversible_command (_("change fade in length"));
3471 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3473 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3479 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3480 XMLNode &before = alist->get_state();
3482 tmp->audio_region()->set_fade_in_length (fade_length);
3483 tmp->audio_region()->set_fade_in_active (true);
3485 XMLNode &after = alist->get_state();
3486 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3489 _editor->commit_reversible_command ();
3493 FadeInDrag::aborted (bool)
3495 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3496 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3502 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3506 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3507 : RegionDrag (e, i, p, v)
3509 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3513 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3515 Drag::start_grab (event, cursor);
3517 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3518 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3519 setup_snap_delta (r->last_frame ());
3521 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3525 FadeOutDrag::setup_pointer_frame_offset ()
3527 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3528 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3529 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3533 FadeOutDrag::motion (GdkEvent* event, bool)
3535 framecnt_t fade_length;
3537 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3538 _editor->snap_to_with_modifier (pos, event);
3539 pos -= snap_delta (event->button.state);
3541 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3543 if (pos > (region->last_frame() - 64)) {
3544 fade_length = 64; // this should really be a minimum fade defined somewhere
3545 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3546 fade_length = region->length() - region->fade_in()->back()->when - 1;
3548 fade_length = region->last_frame() - pos;
3551 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3553 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3559 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3562 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3566 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3568 if (!movement_occurred) {
3572 framecnt_t fade_length;
3574 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3575 _editor->snap_to_with_modifier (pos, event);
3576 pos -= snap_delta (event->button.state);
3578 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3580 if (pos > (region->last_frame() - 64)) {
3581 fade_length = 64; // this should really be a minimum fade defined somewhere
3582 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3583 fade_length = region->length() - region->fade_in()->back()->when - 1;
3585 fade_length = region->last_frame() - pos;
3588 _editor->begin_reversible_command (_("change fade out length"));
3590 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3592 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3598 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3599 XMLNode &before = alist->get_state();
3601 tmp->audio_region()->set_fade_out_length (fade_length);
3602 tmp->audio_region()->set_fade_out_active (true);
3604 XMLNode &after = alist->get_state();
3605 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3608 _editor->commit_reversible_command ();
3612 FadeOutDrag::aborted (bool)
3614 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3615 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3621 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3625 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3628 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3630 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3633 _points.push_back (ArdourCanvas::Duple (0, 0));
3634 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3637 MarkerDrag::~MarkerDrag ()
3639 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3644 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3646 location = new Location (*l);
3647 markers.push_back (m);
3652 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3654 Drag::start_grab (event, cursor);
3658 Location *location = _editor->find_location_from_marker (_marker, is_start);
3659 _editor->_dragging_edit_point = true;
3661 update_item (location);
3663 // _drag_line->show();
3664 // _line->raise_to_top();
3667 show_verbose_cursor_time (location->start());
3669 show_verbose_cursor_time (location->end());
3672 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3675 case Selection::Toggle:
3676 /* we toggle on the button release */
3678 case Selection::Set:
3679 if (!_editor->selection->selected (_marker)) {
3680 _editor->selection->set (_marker);
3683 case Selection::Extend:
3685 Locations::LocationList ll;
3686 list<Marker*> to_add;
3688 _editor->selection->markers.range (s, e);
3689 s = min (_marker->position(), s);
3690 e = max (_marker->position(), e);
3693 if (e < max_framepos) {
3696 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3697 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3698 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3701 to_add.push_back (lm->start);
3704 to_add.push_back (lm->end);
3708 if (!to_add.empty()) {
3709 _editor->selection->add (to_add);
3713 case Selection::Add:
3714 _editor->selection->add (_marker);
3718 /* Set up copies for us to manipulate during the drag
3721 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3723 Location* l = _editor->find_location_from_marker (*i, is_start);
3730 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3732 /* range: check that the other end of the range isn't
3735 CopiedLocationInfo::iterator x;
3736 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3737 if (*(*x).location == *l) {
3741 if (x == _copied_locations.end()) {
3742 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3744 (*x).markers.push_back (*i);
3745 (*x).move_both = true;
3753 MarkerDrag::setup_pointer_frame_offset ()
3756 Location *location = _editor->find_location_from_marker (_marker, is_start);
3757 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3761 MarkerDrag::motion (GdkEvent* event, bool)
3763 framecnt_t f_delta = 0;
3765 bool move_both = false;
3766 Location *real_location;
3767 Location *copy_location = 0;
3769 framepos_t const newframe = adjusted_current_frame (event);
3770 framepos_t next = newframe;
3772 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3776 CopiedLocationInfo::iterator x;
3778 /* find the marker we're dragging, and compute the delta */
3780 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3782 copy_location = (*x).location;
3784 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3786 /* this marker is represented by this
3787 * CopiedLocationMarkerInfo
3790 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3795 if (real_location->is_mark()) {
3796 f_delta = newframe - copy_location->start();
3800 switch (_marker->type()) {
3801 case Marker::SessionStart:
3802 case Marker::RangeStart:
3803 case Marker::LoopStart:
3804 case Marker::PunchIn:
3805 f_delta = newframe - copy_location->start();
3808 case Marker::SessionEnd:
3809 case Marker::RangeEnd:
3810 case Marker::LoopEnd:
3811 case Marker::PunchOut:
3812 f_delta = newframe - copy_location->end();
3815 /* what kind of marker is this ? */
3824 if (x == _copied_locations.end()) {
3825 /* hmm, impossible - we didn't find the dragged marker */
3829 /* now move them all */
3831 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3833 copy_location = x->location;
3835 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3839 if (real_location->locked()) {
3843 if (copy_location->is_mark()) {
3847 copy_location->set_start (copy_location->start() + f_delta);
3851 framepos_t new_start = copy_location->start() + f_delta;
3852 framepos_t new_end = copy_location->end() + f_delta;
3854 if (is_start) { // start-of-range marker
3856 if (move_both || (*x).move_both) {
3857 copy_location->set_start (new_start);
3858 copy_location->set_end (new_end);
3859 } else if (new_start < copy_location->end()) {
3860 copy_location->set_start (new_start);
3861 } else if (newframe > 0) {
3862 _editor->snap_to (next, RoundUpAlways, true);
3863 copy_location->set_end (next);
3864 copy_location->set_start (newframe);
3867 } else { // end marker
3869 if (move_both || (*x).move_both) {
3870 copy_location->set_end (new_end);
3871 copy_location->set_start (new_start);
3872 } else if (new_end > copy_location->start()) {
3873 copy_location->set_end (new_end);
3874 } else if (newframe > 0) {
3875 _editor->snap_to (next, RoundDownAlways, true);
3876 copy_location->set_start (next);
3877 copy_location->set_end (newframe);
3882 update_item (copy_location);
3884 /* now lookup the actual GUI items used to display this
3885 * location and move them to wherever the copy of the location
3886 * is now. This means that the logic in ARDOUR::Location is
3887 * still enforced, even though we are not (yet) modifying
3888 * the real Location itself.
3891 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3894 lm->set_position (copy_location->start(), copy_location->end());
3899 assert (!_copied_locations.empty());
3901 show_verbose_cursor_time (newframe);
3905 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3907 if (!movement_occurred) {
3909 if (was_double_click()) {
3910 _editor->rename_marker (_marker);
3914 /* just a click, do nothing but finish
3915 off the selection process
3918 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3921 case Selection::Set:
3922 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3923 _editor->selection->set (_marker);
3927 case Selection::Toggle:
3928 /* we toggle on the button release, click only */
3929 _editor->selection->toggle (_marker);
3932 case Selection::Extend:
3933 case Selection::Add:
3940 _editor->_dragging_edit_point = false;
3942 _editor->begin_reversible_command ( _("move marker") );
3943 XMLNode &before = _editor->session()->locations()->get_state();
3945 MarkerSelection::iterator i;
3946 CopiedLocationInfo::iterator x;
3949 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3950 x != _copied_locations.end() && i != _editor->selection->markers.end();
3953 Location * location = _editor->find_location_from_marker (*i, is_start);
3957 if (location->locked()) {
3961 if (location->is_mark()) {
3962 location->set_start (((*x).location)->start());
3964 location->set (((*x).location)->start(), ((*x).location)->end());
3969 XMLNode &after = _editor->session()->locations()->get_state();
3970 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3971 _editor->commit_reversible_command ();
3975 MarkerDrag::aborted (bool movement_occured)
3977 if (!movement_occured) {
3981 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3983 /* move all markers to their original location */
3986 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3989 Location * location = _editor->find_location_from_marker (*m, is_start);
3992 (*m)->set_position (is_start ? location->start() : location->end());
3999 MarkerDrag::update_item (Location*)
4004 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4006 _cumulative_x_drag (0),
4007 _cumulative_y_drag (0)
4009 if (_zero_gain_fraction < 0.0) {
4010 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4013 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4015 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4021 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4023 Drag::start_grab (event, _editor->cursors()->fader);
4025 // start the grab at the center of the control point so
4026 // the point doesn't 'jump' to the mouse after the first drag
4027 _fixed_grab_x = _point->get_x();
4028 _fixed_grab_y = _point->get_y();
4030 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4031 setup_snap_delta (pos);
4033 float const fraction = 1 - (_point->get_y() / _point->line().height());
4035 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4037 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4039 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4041 if (!_point->can_slide ()) {
4042 _x_constrained = true;
4047 ControlPointDrag::motion (GdkEvent* event, bool)
4049 double dx = _drags->current_pointer_x() - last_pointer_x();
4050 double dy = current_pointer_y() - last_pointer_y();
4052 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4057 /* coordinate in pixels relative to the start of the region (for region-based automation)
4058 or track (for track-based automation) */
4059 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4060 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4062 // calculate zero crossing point. back off by .01 to stay on the
4063 // positive side of zero
4064 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4066 // make sure we hit zero when passing through
4067 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4071 if (_x_constrained) {
4074 if (_y_constrained) {
4078 _cumulative_x_drag = cx - _fixed_grab_x;
4079 _cumulative_y_drag = cy - _fixed_grab_y;
4083 cy = min ((double) _point->line().height(), cy);
4085 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4087 if (!_x_constrained) {
4088 _editor->snap_to_with_modifier (cx_frames, event);
4091 cx_frames -= snap_delta (event->button.state);
4092 cx_frames = min (cx_frames, _point->line().maximum_time());
4094 float const fraction = 1.0 - (cy / _point->line().height());
4096 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4098 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4102 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4104 if (!movement_occurred) {
4107 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4108 _editor->reset_point_selection ();
4112 motion (event, false);
4115 _point->line().end_drag (_pushing, _final_index);
4116 _editor->commit_reversible_command ();
4120 ControlPointDrag::aborted (bool)
4122 _point->line().reset ();
4126 ControlPointDrag::active (Editing::MouseMode m)
4128 if (m == Editing::MouseDraw) {
4129 /* always active in mouse draw */
4133 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4134 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4137 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4140 , _cumulative_y_drag (0)
4142 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4146 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4148 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4151 _item = &_line->grab_item ();
4153 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4154 origin, and ditto for y.
4157 double cx = event->button.x;
4158 double cy = event->button.y;
4160 _line->parent_group().canvas_to_item (cx, cy);
4162 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4167 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4168 /* no adjacent points */
4172 Drag::start_grab (event, _editor->cursors()->fader);
4174 /* store grab start in parent frame */
4179 double fraction = 1.0 - (cy / _line->height());
4181 _line->start_drag_line (before, after, fraction);
4183 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4187 LineDrag::motion (GdkEvent* event, bool)
4189 double dy = current_pointer_y() - last_pointer_y();
4191 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4195 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4197 _cumulative_y_drag = cy - _fixed_grab_y;
4200 cy = min ((double) _line->height(), cy);
4202 double const fraction = 1.0 - (cy / _line->height());
4205 /* we are ignoring x position for this drag, so we can just pass in anything */
4206 _line->drag_motion (0, fraction, true, false, ignored);
4208 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4212 LineDrag::finished (GdkEvent* event, bool movement_occured)
4214 if (movement_occured) {
4215 motion (event, false);
4216 _line->end_drag (false, 0);
4218 /* add a new control point on the line */
4220 AutomationTimeAxisView* atv;
4222 _line->end_drag (false, 0);
4224 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4225 framepos_t where = _editor->window_event_sample (event, 0, 0);
4226 atv->add_automation_event (event, where, event->button.y, false);
4230 _editor->commit_reversible_command ();
4234 LineDrag::aborted (bool)
4239 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4242 _cumulative_x_drag (0)
4244 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4248 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4250 Drag::start_grab (event);
4252 _line = reinterpret_cast<Line*> (_item);
4255 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4257 double cx = event->button.x;
4258 double cy = event->button.y;
4260 _item->parent()->canvas_to_item (cx, cy);
4262 /* store grab start in parent frame */
4263 _region_view_grab_x = cx;
4265 _before = *(float*) _item->get_data ("position");
4267 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4269 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4273 FeatureLineDrag::motion (GdkEvent*, bool)
4275 double dx = _drags->current_pointer_x() - last_pointer_x();
4277 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4279 _cumulative_x_drag += dx;
4281 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4290 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4292 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4294 float *pos = new float;
4297 _line->set_data ("position", pos);
4303 FeatureLineDrag::finished (GdkEvent*, bool)
4305 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4306 _arv->update_transient(_before, _before);
4310 FeatureLineDrag::aborted (bool)
4315 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4317 , _vertical_only (false)
4319 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4323 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4325 Drag::start_grab (event);
4326 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4330 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4337 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4339 framepos_t grab = grab_frame ();
4340 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4341 _editor->snap_to_with_modifier (grab, event);
4343 grab = raw_grab_frame ();
4346 /* base start and end on initial click position */
4356 if (current_pointer_y() < grab_y()) {
4357 y1 = current_pointer_y();
4360 y2 = current_pointer_y();
4364 if (start != end || y1 != y2) {
4366 double x1 = _editor->sample_to_pixel (start);
4367 double x2 = _editor->sample_to_pixel (end);
4368 const double min_dimension = 2.0;
4370 if (_vertical_only) {
4371 /* fixed 10 pixel width */
4375 x2 = min (x1 - min_dimension, x2);
4377 x2 = max (x1 + min_dimension, x2);
4382 y2 = min (y1 - min_dimension, y2);
4384 y2 = max (y1 + min_dimension, y2);
4387 /* translate rect into item space and set */
4389 ArdourCanvas::Rect r (x1, y1, x2, y2);
4391 /* this drag is a _trackview_only == true drag, so the y1 and
4392 * y2 (computed using current_pointer_y() and grab_y()) will be
4393 * relative to the top of the trackview group). The
4394 * rubberband rect has the same parent/scroll offset as the
4395 * the trackview group, so we can use the "r" rect directly
4396 * to set the shape of the rubberband.
4399 _editor->rubberband_rect->set (r);
4400 _editor->rubberband_rect->show();
4401 _editor->rubberband_rect->raise_to_top();
4403 show_verbose_cursor_time (pf);
4405 do_select_things (event, true);
4410 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4414 framepos_t grab = grab_frame ();
4415 framepos_t lpf = last_pointer_frame ();
4417 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4418 grab = raw_grab_frame ();
4419 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4433 if (current_pointer_y() < grab_y()) {
4434 y1 = current_pointer_y();
4437 y2 = current_pointer_y();
4441 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4445 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4447 if (movement_occurred) {
4449 motion (event, false);
4450 do_select_things (event, false);
4456 bool do_deselect = true;
4457 MidiTimeAxisView* mtv;
4459 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4461 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4462 /* nothing selected */
4463 add_midi_region (mtv);
4464 do_deselect = false;
4468 /* do not deselect if Primary or Tertiary (toggle-select or
4469 * extend-select are pressed.
4472 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4473 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4480 _editor->rubberband_rect->hide();
4484 RubberbandSelectDrag::aborted (bool)
4486 _editor->rubberband_rect->hide ();
4489 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4490 : RegionDrag (e, i, p, v)
4492 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4496 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4498 Drag::start_grab (event, cursor);
4500 _editor->get_selection().add (_primary);
4502 framepos_t where = _primary->region()->position();
4503 setup_snap_delta (where);
4505 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4509 TimeFXDrag::motion (GdkEvent* event, bool)
4511 RegionView* rv = _primary;
4512 StreamView* cv = rv->get_time_axis_view().view ();
4514 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4515 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4516 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4517 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4518 _editor->snap_to_with_modifier (pf, event);
4519 pf -= snap_delta (event->button.state);
4521 if (pf > rv->region()->position()) {
4522 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4525 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4529 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4531 _primary->get_time_axis_view().hide_timestretch ();
4533 if (!movement_occurred) {
4537 if (last_pointer_frame() < _primary->region()->position()) {
4538 /* backwards drag of the left edge - not usable */
4542 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4544 float percentage = (double) newlen / (double) _primary->region()->length();
4546 #ifndef USE_RUBBERBAND
4547 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4548 if (_primary->region()->data_type() == DataType::AUDIO) {
4549 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4553 if (!_editor->get_selection().regions.empty()) {
4554 /* primary will already be included in the selection, and edit
4555 group shared editing will propagate selection across
4556 equivalent regions, so just use the current region
4560 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4561 error << _("An error occurred while executing time stretch operation") << endmsg;
4567 TimeFXDrag::aborted (bool)
4569 _primary->get_time_axis_view().hide_timestretch ();
4572 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4575 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4579 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4581 Drag::start_grab (event);
4585 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4587 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4591 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4593 if (movement_occurred && _editor->session()) {
4594 /* make sure we stop */
4595 _editor->session()->request_transport_speed (0.0);
4600 ScrubDrag::aborted (bool)
4605 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4609 , _time_selection_at_start (!_editor->get_selection().time.empty())
4611 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4613 if (_time_selection_at_start) {
4614 start_at_start = _editor->get_selection().time.start();
4615 end_at_start = _editor->get_selection().time.end_frame();
4620 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4622 if (_editor->session() == 0) {
4626 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4628 switch (_operation) {
4629 case CreateSelection:
4630 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4635 cursor = _editor->cursors()->selector;
4636 Drag::start_grab (event, cursor);
4639 case SelectionStartTrim:
4640 if (_editor->clicked_axisview) {
4641 _editor->clicked_axisview->order_selection_trims (_item, true);
4643 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4646 case SelectionEndTrim:
4647 if (_editor->clicked_axisview) {
4648 _editor->clicked_axisview->order_selection_trims (_item, false);
4650 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4654 Drag::start_grab (event, cursor);
4657 case SelectionExtend:
4658 Drag::start_grab (event, cursor);
4662 if (_operation == SelectionMove) {
4663 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4665 show_verbose_cursor_time (adjusted_current_frame (event));
4670 SelectionDrag::setup_pointer_frame_offset ()
4672 switch (_operation) {
4673 case CreateSelection:
4674 _pointer_frame_offset = 0;
4677 case SelectionStartTrim:
4679 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4682 case SelectionEndTrim:
4683 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4686 case SelectionExtend:
4692 SelectionDrag::motion (GdkEvent* event, bool first_move)
4694 framepos_t start = 0;
4696 framecnt_t length = 0;
4697 framecnt_t distance = 0;
4699 framepos_t const pending_position = adjusted_current_frame (event);
4701 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4705 switch (_operation) {
4706 case CreateSelection:
4708 framepos_t grab = grab_frame ();
4711 grab = adjusted_current_frame (event, false);
4712 if (grab < pending_position) {
4713 _editor->snap_to (grab, RoundDownMaybe);
4715 _editor->snap_to (grab, RoundUpMaybe);
4719 if (pending_position < grab) {
4720 start = pending_position;
4723 end = pending_position;
4727 /* first drag: Either add to the selection
4728 or create a new selection
4735 /* adding to the selection */
4736 _editor->set_selected_track_as_side_effect (Selection::Add);
4737 _editor->clicked_selection = _editor->selection->add (start, end);
4744 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4745 _editor->set_selected_track_as_side_effect (Selection::Set);
4748 _editor->clicked_selection = _editor->selection->set (start, end);
4752 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4753 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4754 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4756 _editor->selection->add (atest);
4760 /* select all tracks within the rectangle that we've marked out so far */
4761 TrackViewList new_selection;
4762 TrackViewList& all_tracks (_editor->track_views);
4764 ArdourCanvas::Coord const top = grab_y();
4765 ArdourCanvas::Coord const bottom = current_pointer_y();
4767 if (top >= 0 && bottom >= 0) {
4769 //first, find the tracks that are covered in the y range selection
4770 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4771 if ((*i)->covered_by_y_range (top, bottom)) {
4772 new_selection.push_back (*i);
4776 //now find any tracks that are GROUPED with the tracks we selected
4777 TrackViewList grouped_add = new_selection;
4778 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4779 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4780 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4781 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4782 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4783 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4784 grouped_add.push_back (*j);
4789 //now compare our list with the current selection, and add or remove as necessary
4790 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4791 TrackViewList tracks_to_add;
4792 TrackViewList tracks_to_remove;
4793 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4794 if ( !_editor->selection->tracks.contains ( *i ) )
4795 tracks_to_add.push_back ( *i );
4796 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4797 if ( !grouped_add.contains ( *i ) )
4798 tracks_to_remove.push_back ( *i );
4799 _editor->selection->add(tracks_to_add);
4800 _editor->selection->remove(tracks_to_remove);
4806 case SelectionStartTrim:
4808 start = _editor->selection->time[_editor->clicked_selection].start;
4809 end = _editor->selection->time[_editor->clicked_selection].end;
4811 if (pending_position > end) {
4814 start = pending_position;
4818 case SelectionEndTrim:
4820 start = _editor->selection->time[_editor->clicked_selection].start;
4821 end = _editor->selection->time[_editor->clicked_selection].end;
4823 if (pending_position < start) {
4826 end = pending_position;
4833 start = _editor->selection->time[_editor->clicked_selection].start;
4834 end = _editor->selection->time[_editor->clicked_selection].end;
4836 length = end - start;
4837 distance = pending_position - start;
4838 start = pending_position;
4839 _editor->snap_to (start);
4841 end = start + length;
4845 case SelectionExtend:
4850 switch (_operation) {
4852 if (_time_selection_at_start) {
4853 _editor->selection->move_time (distance);
4857 _editor->selection->replace (_editor->clicked_selection, start, end);
4861 if (_operation == SelectionMove) {
4862 show_verbose_cursor_time(start);
4864 show_verbose_cursor_time(pending_position);
4869 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4871 Session* s = _editor->session();
4873 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4874 if (movement_occurred) {
4875 motion (event, false);
4876 /* XXX this is not object-oriented programming at all. ick */
4877 if (_editor->selection->time.consolidate()) {
4878 _editor->selection->TimeChanged ();
4881 /* XXX what if its a music time selection? */
4883 if ( s->get_play_range() && s->transport_rolling() ) {
4884 s->request_play_range (&_editor->selection->time, true);
4886 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4887 if (_operation == SelectionEndTrim)
4888 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4890 s->request_locate (_editor->get_selection().time.start());
4896 /* just a click, no pointer movement.
4899 if (_operation == SelectionExtend) {
4900 if (_time_selection_at_start) {
4901 framepos_t pos = adjusted_current_frame (event, false);
4902 framepos_t start = min (pos, start_at_start);
4903 framepos_t end = max (pos, end_at_start);
4904 _editor->selection->set (start, end);
4907 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4908 if (_editor->clicked_selection) {
4909 _editor->selection->remove (_editor->clicked_selection);
4912 if (!_editor->clicked_selection) {
4913 _editor->selection->clear_time();
4918 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4919 _editor->selection->set (_editor->clicked_axisview);
4922 if (s && s->get_play_range () && s->transport_rolling()) {
4923 s->request_stop (false, false);
4928 _editor->stop_canvas_autoscroll ();
4929 _editor->clicked_selection = 0;
4930 _editor->commit_reversible_selection_op ();
4934 SelectionDrag::aborted (bool)
4939 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4940 : Drag (e, i, false),
4944 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4946 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4947 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4948 physical_screen_height (_editor->get_window())));
4949 _drag_rect->hide ();
4951 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4952 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4955 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4957 /* normal canvas items will be cleaned up when their parent group is deleted. But
4958 this item is created as the child of a long-lived parent group, and so we
4959 need to explicitly delete it.
4965 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4967 if (_editor->session() == 0) {
4971 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4973 if (!_editor->temp_location) {
4974 _editor->temp_location = new Location (*_editor->session());
4977 switch (_operation) {
4978 case CreateSkipMarker:
4979 case CreateRangeMarker:
4980 case CreateTransportMarker:
4981 case CreateCDMarker:
4983 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4988 cursor = _editor->cursors()->selector;
4992 Drag::start_grab (event, cursor);
4994 show_verbose_cursor_time (adjusted_current_frame (event));
4998 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5000 framepos_t start = 0;
5002 ArdourCanvas::Rectangle *crect;
5004 switch (_operation) {
5005 case CreateSkipMarker:
5006 crect = _editor->range_bar_drag_rect;
5008 case CreateRangeMarker:
5009 crect = _editor->range_bar_drag_rect;
5011 case CreateTransportMarker:
5012 crect = _editor->transport_bar_drag_rect;
5014 case CreateCDMarker:
5015 crect = _editor->cd_marker_bar_drag_rect;
5018 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5023 framepos_t const pf = adjusted_current_frame (event);
5025 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5026 framepos_t grab = grab_frame ();
5027 _editor->snap_to (grab);
5029 if (pf < grab_frame()) {
5037 /* first drag: Either add to the selection
5038 or create a new selection.
5043 _editor->temp_location->set (start, end);
5047 update_item (_editor->temp_location);
5049 //_drag_rect->raise_to_top();
5055 _editor->temp_location->set (start, end);
5057 double x1 = _editor->sample_to_pixel (start);
5058 double x2 = _editor->sample_to_pixel (end);
5062 update_item (_editor->temp_location);
5065 show_verbose_cursor_time (pf);
5070 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5072 Location * newloc = 0;
5076 if (movement_occurred) {
5077 motion (event, false);
5080 switch (_operation) {
5081 case CreateSkipMarker:
5082 case CreateRangeMarker:
5083 case CreateCDMarker:
5085 XMLNode &before = _editor->session()->locations()->get_state();
5086 if (_operation == CreateSkipMarker) {
5087 _editor->begin_reversible_command (_("new skip marker"));
5088 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5089 flags = Location::IsRangeMarker | Location::IsSkip;
5090 _editor->range_bar_drag_rect->hide();
5091 } else if (_operation == CreateCDMarker) {
5092 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5093 _editor->begin_reversible_command (_("new CD marker"));
5094 flags = Location::IsRangeMarker | Location::IsCDMarker;
5095 _editor->cd_marker_bar_drag_rect->hide();
5097 _editor->begin_reversible_command (_("new skip marker"));
5098 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5099 flags = Location::IsRangeMarker;
5100 _editor->range_bar_drag_rect->hide();
5102 newloc = new Location (
5103 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5106 _editor->session()->locations()->add (newloc, true);
5107 XMLNode &after = _editor->session()->locations()->get_state();
5108 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5109 _editor->commit_reversible_command ();
5113 case CreateTransportMarker:
5114 // popup menu to pick loop or punch
5115 _editor->new_transport_marker_context_menu (&event->button, _item);
5121 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5123 if (_operation == CreateTransportMarker) {
5125 /* didn't drag, so just locate */
5127 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5129 } else if (_operation == CreateCDMarker) {
5131 /* didn't drag, but mark is already created so do
5134 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5139 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5141 if (end == max_framepos) {
5142 end = _editor->session()->current_end_frame ();
5145 if (start == max_framepos) {
5146 start = _editor->session()->current_start_frame ();
5149 switch (_editor->mouse_mode) {
5151 /* find the two markers on either side and then make the selection from it */
5152 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5156 /* find the two markers on either side of the click and make the range out of it */
5157 _editor->selection->set (start, end);
5166 _editor->stop_canvas_autoscroll ();
5170 RangeMarkerBarDrag::aborted (bool movement_occured)
5172 if (movement_occured) {
5173 _drag_rect->hide ();
5178 RangeMarkerBarDrag::update_item (Location* location)
5180 double const x1 = _editor->sample_to_pixel (location->start());
5181 double const x2 = _editor->sample_to_pixel (location->end());
5183 _drag_rect->set_x0 (x1);
5184 _drag_rect->set_x1 (x2);
5187 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5189 , _cumulative_dx (0)
5190 , _cumulative_dy (0)
5192 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5194 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5196 _region = &_primary->region_view ();
5197 _note_height = _region->midi_stream_view()->note_height ();
5201 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5203 Drag::start_grab (event);
5204 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5206 if (!(_was_selected = _primary->selected())) {
5208 /* tertiary-click means extend selection - we'll do that on button release,
5209 so don't add it here, because otherwise we make it hard to figure
5210 out the "extend-to" range.
5213 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5216 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5219 _region->note_selected (_primary, true);
5221 _region->unique_select (_primary);
5224 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5225 _editor->commit_reversible_selection_op();
5230 /** @return Current total drag x change in frames */
5232 NoteDrag::total_dx (const guint state) const
5235 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5237 /* primary note time */
5238 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5240 /* new time of the primary note in session frames */
5241 frameoffset_t st = n + dx + snap_delta (state);
5243 framepos_t const rp = _region->region()->position ();
5245 /* prevent the note being dragged earlier than the region's position */
5248 /* possibly snap and return corresponding delta */
5252 if (ArdourKeyboard::indicates_snap (state)) {
5253 if (_editor->snap_mode () != SnapOff) {
5257 if (_editor->snap_mode () == SnapOff) {
5259 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5260 if (ArdourKeyboard::indicates_snap_delta (state)) {
5266 return _region->snap_frame_to_frame (st - rp, snap) + rp - n - snap_delta (state);
5269 /** @return Current total drag y change in note number */
5271 NoteDrag::total_dy () const
5273 MidiStreamView* msv = _region->midi_stream_view ();
5274 double const y = _region->midi_view()->y_position ();
5275 /* new current note */
5276 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5278 n = max (msv->lowest_note(), n);
5279 n = min (msv->highest_note(), n);
5280 /* and work out delta */
5281 return n - msv->y_to_note (grab_y() - y);
5285 NoteDrag::motion (GdkEvent * event, bool)
5287 /* Total change in x and y since the start of the drag */
5288 frameoffset_t const dx = total_dx (event->button.state);
5289 int8_t const dy = total_dy ();
5291 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5292 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5293 double const tdy = -dy * _note_height - _cumulative_dy;
5296 _cumulative_dx += tdx;
5297 _cumulative_dy += tdy;
5299 int8_t note_delta = total_dy();
5301 _region->move_selection (tdx, tdy, note_delta);
5303 /* the new note value may be the same as the old one, but we
5304 * don't know what that means because the selection may have
5305 * involved more than one note and we might be doing something
5306 * odd with them. so show the note value anyway, always.
5310 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5312 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5313 (int) floor ((double)new_note));
5315 show_verbose_cursor_text (buf);
5320 NoteDrag::finished (GdkEvent* ev, bool moved)
5323 /* no motion - select note */
5325 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5326 _editor->current_mouse_mode() == Editing::MouseDraw) {
5328 bool changed = false;
5330 if (_was_selected) {
5331 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5333 _region->note_deselected (_primary);
5337 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5338 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5340 if (!extend && !add && _region->selection_size() > 1) {
5341 _region->unique_select (_primary);
5343 } else if (extend) {
5344 _region->note_selected (_primary, true, true);
5347 /* it was added during button press */
5352 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5353 _editor->commit_reversible_selection_op();
5357 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5362 NoteDrag::aborted (bool)
5367 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5368 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5369 : Drag (editor, atv->base_item ())
5371 , _y_origin (atv->y_position())
5372 , _nothing_to_drag (false)
5374 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5375 setup (atv->lines ());
5378 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5379 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5380 : Drag (editor, rv->get_canvas_group ())
5382 , _y_origin (rv->get_time_axis_view().y_position())
5383 , _nothing_to_drag (false)
5386 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5388 list<boost::shared_ptr<AutomationLine> > lines;
5390 AudioRegionView* audio_view;
5391 AutomationRegionView* automation_view;
5392 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5393 lines.push_back (audio_view->get_gain_line ());
5394 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5395 lines.push_back (automation_view->line ());
5398 error << _("Automation range drag created for invalid region type") << endmsg;
5404 /** @param lines AutomationLines to drag.
5405 * @param offset Offset from the session start to the points in the AutomationLines.
5408 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5410 /* find the lines that overlap the ranges being dragged */
5411 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5412 while (i != lines.end ()) {
5413 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5416 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5418 /* check this range against all the AudioRanges that we are using */
5419 list<AudioRange>::const_iterator k = _ranges.begin ();
5420 while (k != _ranges.end()) {
5421 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5427 /* add it to our list if it overlaps at all */
5428 if (k != _ranges.end()) {
5433 _lines.push_back (n);
5439 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5443 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5445 return 1.0 - ((global_y - _y_origin) / line->height());
5449 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5451 const double v = list->eval(x);
5452 return _integral ? rint(v) : v;
5456 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5458 Drag::start_grab (event, cursor);
5460 /* Get line states before we start changing things */
5461 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5462 i->state = &i->line->get_state ();
5463 i->original_fraction = y_fraction (i->line, current_pointer_y());
5466 if (_ranges.empty()) {
5468 /* No selected time ranges: drag all points */
5469 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5470 uint32_t const N = i->line->npoints ();
5471 for (uint32_t j = 0; j < N; ++j) {
5472 i->points.push_back (i->line->nth (j));
5478 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5480 framecnt_t const half = (i->start + i->end) / 2;
5482 /* find the line that this audio range starts in */
5483 list<Line>::iterator j = _lines.begin();
5484 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5488 if (j != _lines.end()) {
5489 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5491 /* j is the line that this audio range starts in; fade into it;
5492 64 samples length plucked out of thin air.
5495 framepos_t a = i->start + 64;
5500 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5501 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5503 the_list->editor_add (p, value (the_list, p));
5504 the_list->editor_add (q, value (the_list, q));
5507 /* same thing for the end */
5510 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5514 if (j != _lines.end()) {
5515 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5517 /* j is the line that this audio range starts in; fade out of it;
5518 64 samples length plucked out of thin air.
5521 framepos_t b = i->end - 64;
5526 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5527 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5529 the_list->editor_add (p, value (the_list, p));
5530 the_list->editor_add (q, value (the_list, q));
5534 _nothing_to_drag = true;
5536 /* Find all the points that should be dragged and put them in the relevant
5537 points lists in the Line structs.
5540 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5542 uint32_t const N = i->line->npoints ();
5543 for (uint32_t j = 0; j < N; ++j) {
5545 /* here's a control point on this line */
5546 ControlPoint* p = i->line->nth (j);
5547 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5549 /* see if it's inside a range */
5550 list<AudioRange>::const_iterator k = _ranges.begin ();
5551 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5555 if (k != _ranges.end()) {
5556 /* dragging this point */
5557 _nothing_to_drag = false;
5558 i->points.push_back (p);
5564 if (_nothing_to_drag) {
5568 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5569 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5574 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5576 if (_nothing_to_drag) {
5580 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5581 float const f = y_fraction (l->line, current_pointer_y());
5582 /* we are ignoring x position for this drag, so we can just pass in anything */
5584 l->line->drag_motion (0, f, true, false, ignored);
5585 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5590 AutomationRangeDrag::finished (GdkEvent* event, bool)
5592 if (_nothing_to_drag) {
5596 motion (event, false);
5597 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5598 i->line->end_drag (false, 0);
5601 _editor->commit_reversible_command ();
5605 AutomationRangeDrag::aborted (bool)
5607 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5612 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5614 , initial_time_axis_view (itav)
5616 /* note that time_axis_view may be null if the regionview was created
5617 * as part of a copy operation.
5619 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5620 layer = v->region()->layer ();
5621 initial_y = v->get_canvas_group()->position().y;
5622 initial_playlist = v->region()->playlist ();
5623 initial_position = v->region()->position ();
5624 initial_end = v->region()->position () + v->region()->length ();
5627 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5628 : Drag (e, i->canvas_item ())
5631 , _cumulative_dx (0)
5633 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5634 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5639 PatchChangeDrag::motion (GdkEvent* ev, bool)
5641 framepos_t f = adjusted_current_frame (ev);
5642 boost::shared_ptr<Region> r = _region_view->region ();
5643 f = max (f, r->position ());
5644 f = min (f, r->last_frame ());
5646 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5647 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5648 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5649 _cumulative_dx = dxu;
5653 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5655 if (!movement_occurred) {
5659 boost::shared_ptr<Region> r (_region_view->region ());
5660 framepos_t f = adjusted_current_frame (ev);
5661 f = max (f, r->position ());
5662 f = min (f, r->last_frame ());
5664 _region_view->move_patch_change (
5666 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5671 PatchChangeDrag::aborted (bool)
5673 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5677 PatchChangeDrag::setup_pointer_frame_offset ()
5679 boost::shared_ptr<Region> region = _region_view->region ();
5680 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5683 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5684 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5691 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5693 _region_view->update_drag_selection (
5695 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5699 MidiRubberbandSelectDrag::deselect_things ()
5704 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5705 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5708 _vertical_only = true;
5712 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5714 double const y = _region_view->midi_view()->y_position ();
5716 y1 = max (0.0, y1 - y);
5717 y2 = max (0.0, y2 - y);
5719 _region_view->update_vertical_drag_selection (
5722 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5727 MidiVerticalSelectDrag::deselect_things ()
5732 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5733 : RubberbandSelectDrag (e, i)
5739 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5741 if (drag_in_progress) {
5742 /* We just want to select things at the end of the drag, not during it */
5746 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5748 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5750 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5752 _editor->commit_reversible_selection_op ();
5756 EditorRubberbandSelectDrag::deselect_things ()
5758 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5760 _editor->selection->clear_tracks();
5761 _editor->selection->clear_regions();
5762 _editor->selection->clear_points ();
5763 _editor->selection->clear_lines ();
5764 _editor->selection->clear_midi_notes ();
5766 _editor->commit_reversible_selection_op();
5769 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5774 _note[0] = _note[1] = 0;
5777 NoteCreateDrag::~NoteCreateDrag ()
5783 NoteCreateDrag::grid_frames (framepos_t t) const
5786 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5788 grid_beats = Evoral::Beats(1);
5791 return _region_view->region_beats_to_region_frames (grid_beats);
5795 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5797 Drag::start_grab (event, cursor);
5799 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5801 framepos_t pf = _drags->current_pointer_frame ();
5802 framecnt_t const g = grid_frames (pf);
5804 /* Hack so that we always snap to the note that we are over, instead of snapping
5805 to the next one if we're more than halfway through the one we're over.
5807 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5811 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5812 _note[1] = _note[0];
5814 MidiStreamView* sv = _region_view->midi_stream_view ();
5815 double const x = _editor->sample_to_pixel (_note[0]);
5816 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5818 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5819 _drag_rect->set_outline_all ();
5820 _drag_rect->set_outline_color (0xffffff99);
5821 _drag_rect->set_fill_color (0xffffff66);
5825 NoteCreateDrag::motion (GdkEvent* event, bool)
5827 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5828 double const x0 = _editor->sample_to_pixel (_note[0]);
5829 double const x1 = _editor->sample_to_pixel (_note[1]);
5830 _drag_rect->set_x0 (std::min(x0, x1));
5831 _drag_rect->set_x1 (std::max(x0, x1));
5835 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5837 if (!had_movement) {
5841 framepos_t const start = min (_note[0], _note[1]);
5842 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5844 framecnt_t const g = grid_frames (start);
5845 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5847 if (_editor->snap_mode() == SnapNormal && length < g) {
5851 Evoral::Beats length_beats = max (
5852 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5854 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5858 NoteCreateDrag::y_to_region (double y) const
5861 _region_view->get_canvas_group()->canvas_to_item (x, y);
5866 NoteCreateDrag::aborted (bool)
5871 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5876 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5880 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5882 Drag::start_grab (event, cursor);
5886 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5892 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5895 distance = _drags->current_pointer_x() - grab_x();
5896 len = ar->fade_in()->back()->when;
5898 distance = grab_x() - _drags->current_pointer_x();
5899 len = ar->fade_out()->back()->when;
5902 /* how long should it be ? */
5904 new_length = len + _editor->pixel_to_sample (distance);
5906 /* now check with the region that this is legal */
5908 new_length = ar->verify_xfade_bounds (new_length, start);
5911 arv->reset_fade_in_shape_width (ar, new_length);
5913 arv->reset_fade_out_shape_width (ar, new_length);
5918 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5924 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5927 distance = _drags->current_pointer_x() - grab_x();
5928 len = ar->fade_in()->back()->when;
5930 distance = grab_x() - _drags->current_pointer_x();
5931 len = ar->fade_out()->back()->when;
5934 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5936 _editor->begin_reversible_command ("xfade trim");
5937 ar->playlist()->clear_owned_changes ();
5940 ar->set_fade_in_length (new_length);
5942 ar->set_fade_out_length (new_length);
5945 /* Adjusting the xfade may affect other regions in the playlist, so we need
5946 to get undo Commands from the whole playlist rather than just the
5950 vector<Command*> cmds;
5951 ar->playlist()->rdiff (cmds);
5952 _editor->session()->add_commands (cmds);
5953 _editor->commit_reversible_command ();
5958 CrossfadeEdgeDrag::aborted (bool)
5961 // arv->redraw_start_xfade ();
5963 // arv->redraw_end_xfade ();
5967 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5968 : Drag (e, item, true)
5969 , line (new EditorCursor (*e))
5971 line->set_position (pos);
5975 RegionCutDrag::~RegionCutDrag ()
5981 RegionCutDrag::motion (GdkEvent*, bool)
5983 framepos_t where = _drags->current_pointer_frame();
5984 _editor->snap_to (where);
5986 line->set_position (where);
5990 RegionCutDrag::finished (GdkEvent*, bool)
5992 _editor->get_track_canvas()->canvas()->re_enter();
5994 framepos_t pos = _drags->current_pointer_frame();
5998 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6004 _editor->split_regions_at (pos, rs);
6008 RegionCutDrag::aborted (bool)