2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "ui_config.h"
68 #include "verbose_cursor.h"
71 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
82 DragManager::DragManager (Editor* e)
85 , _current_pointer_x (0.0)
86 , _current_pointer_y (0.0)
87 , _current_pointer_frame (0)
88 , _old_follow_playhead (false)
92 DragManager::~DragManager ()
97 /** Call abort for each active drag */
103 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 if (!_drags.empty ()) {
109 _editor->set_follow_playhead (_old_follow_playhead, false);
113 _editor->abort_reversible_command();
119 DragManager::add (Drag* d)
121 d->set_manager (this);
122 _drags.push_back (d);
126 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
128 d->set_manager (this);
129 _drags.push_back (d);
134 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
136 /* Prevent follow playhead during the drag to be nice to the user */
137 _old_follow_playhead = _editor->follow_playhead ();
138 _editor->set_follow_playhead (false);
140 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
142 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
143 (*i)->start_grab (e, c);
147 /** Call end_grab for each active drag.
148 * @return true if any drag reported movement having occurred.
151 DragManager::end_grab (GdkEvent* e)
156 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
157 bool const t = (*i)->end_grab (e);
168 _editor->set_follow_playhead (_old_follow_playhead, false);
174 DragManager::mark_double_click ()
176 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
177 (*i)->set_double_click (true);
182 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
186 /* calling this implies that we expect the event to have canvas
189 * Can we guarantee that this is true?
192 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
194 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
195 bool const t = (*i)->motion_handler (e, from_autoscroll);
196 /* run all handlers; return true if at least one of them
197 returns true (indicating that the event has been handled).
209 DragManager::have_item (ArdourCanvas::Item* i) const
211 list<Drag*>::const_iterator j = _drags.begin ();
212 while (j != _drags.end() && (*j)->item () != i) {
216 return j != _drags.end ();
219 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
223 , _pointer_frame_offset (0)
224 , _x_constrained (false)
225 , _y_constrained (false)
226 , _was_rolling (false)
227 , _trackview_only (trackview_only)
228 , _move_threshold_passed (false)
229 , _starting_point_passed (false)
230 , _initially_vertical (false)
231 , _was_double_click (false)
234 , _last_pointer_x (0.0)
235 , _last_pointer_y (0.0)
236 , _raw_grab_frame (0)
238 , _last_pointer_frame (0)
245 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
251 _cursor_ctx = CursorContext::create (*_editor, cursor);
253 _cursor_ctx->change (cursor);
260 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
263 /* we set up x/y dragging constraints on first move */
265 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::snap_delta (guint state) const
349 if (ArdourKeyboard::indicates_snap_delta (state)) {
357 Drag::current_pointer_x() const
359 return _drags->current_pointer_x ();
363 Drag::current_pointer_y () const
365 if (!_trackview_only) {
366 return _drags->current_pointer_y ();
369 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
373 Drag::setup_snap_delta (framepos_t pos)
375 framepos_t temp = pos;
376 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
377 _snap_delta = temp - pos;
381 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
383 /* check to see if we have moved in any way that matters since the last motion event */
384 if (_move_threshold_passed &&
385 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
386 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
390 pair<framecnt_t, int> const threshold = move_threshold ();
392 bool const old_move_threshold_passed = _move_threshold_passed;
394 if (!_move_threshold_passed) {
396 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
397 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
399 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
402 if (active (_editor->mouse_mode) && _move_threshold_passed) {
404 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
406 if (old_move_threshold_passed != _move_threshold_passed) {
410 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
411 _initially_vertical = true;
413 _initially_vertical = false;
415 /** check constraints for this drag.
416 * Note that the current convention is to use "contains" for
417 * key modifiers during motion and "equals" when initiating a drag.
418 * In this case we haven't moved yet, so "equals" applies here.
420 if (Config->get_edit_mode() != Lock) {
421 if (event->motion.state & Gdk::BUTTON2_MASK) {
422 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
423 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
424 _x_constrained = false;
425 _y_constrained = true;
427 _x_constrained = true;
428 _y_constrained = false;
430 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
431 // if dragging normally, the motion is constrained to the first direction of movement.
432 if (_initially_vertical) {
433 _x_constrained = true;
434 _y_constrained = false;
436 _x_constrained = false;
437 _y_constrained = true;
441 if (event->button.state & Gdk::BUTTON2_MASK) {
442 _x_constrained = false;
444 _x_constrained = true;
446 _y_constrained = false;
450 if (!from_autoscroll) {
451 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
454 if (!_editor->autoscroll_active() || from_autoscroll) {
457 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
459 motion (event, first_move && !_starting_point_passed);
461 if (first_move && !_starting_point_passed) {
462 _starting_point_passed = true;
465 _last_pointer_x = _drags->current_pointer_x ();
466 _last_pointer_y = current_pointer_y ();
467 _last_pointer_frame = adjusted_current_frame (event, false);
477 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
485 aborted (_move_threshold_passed);
487 _editor->stop_canvas_autoscroll ();
488 _editor->verbose_cursor()->hide ();
492 Drag::show_verbose_cursor_time (framepos_t frame)
494 _editor->verbose_cursor()->set_time (frame);
495 _editor->verbose_cursor()->show ();
499 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
501 _editor->verbose_cursor()->set_duration (start, end);
502 _editor->verbose_cursor()->show ();
506 Drag::show_verbose_cursor_text (string const & text)
508 _editor->verbose_cursor()->set (text);
509 _editor->verbose_cursor()->show ();
512 boost::shared_ptr<Region>
513 Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
515 if (_editor->session()) {
516 const TempoMap& map (_editor->session()->tempo_map());
517 framecnt_t pos = grab_frame();
518 /* not that the frame rate used here can be affected by pull up/down which
521 framecnt_t len = map.frame_at_beat (map.beat_at_frame (pos) + 1.0) - pos;
522 return view->add_region (grab_frame(), len, commit);
525 return boost::shared_ptr<Region>();
528 struct PresentationInfoTimeAxisViewSorter {
529 bool operator() (TimeAxisView* a, TimeAxisView* b) {
530 return a->presentation_info().order() < b->presentation_info().order();
534 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
539 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
541 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
542 as some of the regions we are dragging may be on such tracks.
545 TrackViewList track_views = _editor->track_views;
546 track_views.sort (PresentationInfoTimeAxisViewSorter ());
548 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
549 _time_axis_views.push_back (*i);
551 TimeAxisView::Children children_list = (*i)->get_child_list ();
552 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
553 _time_axis_views.push_back (j->get());
557 /* the list of views can be empty at this point if this is a region list-insert drag
560 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
561 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
564 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
568 RegionDrag::region_going_away (RegionView* v)
570 list<DraggingView>::iterator i = _views.begin ();
571 while (i != _views.end() && i->view != v) {
575 if (i != _views.end()) {
580 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
581 * or -1 if it is not found.
584 RegionDrag::find_time_axis_view (TimeAxisView* t) const
587 int const N = _time_axis_views.size ();
588 while (i < N && _time_axis_views[i] != t) {
592 if (_time_axis_views[i] != t) {
599 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
600 : RegionDrag (e, i, p, v)
602 , _ignore_video_lock (false)
604 , _last_pointer_time_axis_view (0)
605 , _last_pointer_layer (0)
610 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
614 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
616 Drag::start_grab (event, cursor);
617 setup_snap_delta (_last_frame_position);
619 show_verbose_cursor_time (_last_frame_position);
621 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
623 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
624 assert(_last_pointer_time_axis_view >= 0);
625 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
628 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
629 _ignore_video_lock = true;
633 /* cross track dragging seems broken here. disabled for now. */
634 _y_constrained = true;
639 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
641 /* compute the amount of pointer motion in frames, and where
642 the region would be if we moved it by that much.
644 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
646 framepos_t sync_frame;
647 framecnt_t sync_offset;
650 sync_offset = _primary->region()->sync_offset (sync_dir);
652 /* we don't handle a sync point that lies before zero.
654 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
656 framecnt_t const sd = snap_delta (event->button.state);
657 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
659 _editor->snap_to_with_modifier (sync_frame, event);
661 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
664 *pending_region_position = _last_frame_position;
667 if (*pending_region_position > max_framepos - _primary->region()->length()) {
668 *pending_region_position = _last_frame_position;
673 bool const x_move_allowed = !_x_constrained;
675 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
677 /* x movement since last time (in pixels) */
678 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
680 /* total x movement */
681 framecnt_t total_dx = *pending_region_position;
682 if (regions_came_from_canvas()) {
683 total_dx = total_dx - grab_frame ();
686 /* check that no regions have gone off the start of the session */
687 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
688 if ((i->view->region()->position() + total_dx) < 0) {
690 *pending_region_position = _last_frame_position;
701 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
707 const int tavsize = _time_axis_views.size();
708 const int dt = delta > 0 ? +1 : -1;
710 int target = start + delta - skip;
712 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
713 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
715 while (current >= 0 && current != target) {
717 if (current < 0 && dt < 0) {
720 if (current >= tavsize && dt > 0) {
723 if (current < 0 || current >= tavsize) {
727 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
728 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
732 if (distance_only && current == start + delta) {
740 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
742 if (_y_constrained) {
746 const int tavsize = _time_axis_views.size();
747 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
748 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
749 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
751 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
752 /* already in the drop zone */
753 if (delta_track >= 0) {
754 /* downward motion - OK if others are still not in the dropzone */
763 } else if (n >= tavsize) {
764 /* downward motion into drop zone. That's fine. */
768 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
769 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
770 /* not a track, or the wrong type */
774 double const l = i->layer + delta_layer;
776 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
777 mode to allow the user to place a region below another on layer 0.
779 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
780 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
781 If it has, the layers will be munged later anyway, so it's ok.
787 /* all regions being dragged are ok with this change */
791 struct DraggingViewSorter {
792 bool operator() (const DraggingView& a, const DraggingView& b) {
793 return a.time_axis_view < b.time_axis_view;
798 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
800 double delta_layer = 0;
801 int delta_time_axis_view = 0;
802 int current_pointer_time_axis_view = -1;
804 assert (!_views.empty ());
806 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
808 /* Find the TimeAxisView that the pointer is now over */
809 const double cur_y = current_pointer_y ();
810 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
811 TimeAxisView* tv = r.first;
813 if (!tv && cur_y < 0) {
814 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
818 /* find drop-zone y-position */
819 Coord last_track_bottom_edge;
820 last_track_bottom_edge = 0;
821 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
822 if (!(*t)->hidden()) {
823 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
828 if (tv && tv->view()) {
829 /* the mouse is over a track */
830 double layer = r.second;
832 if (first_move && tv->view()->layer_display() == Stacked) {
833 tv->view()->set_layer_display (Expanded);
836 /* Here's the current pointer position in terms of time axis view and layer */
837 current_pointer_time_axis_view = find_time_axis_view (tv);
838 assert(current_pointer_time_axis_view >= 0);
840 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
842 /* Work out the change in y */
844 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
845 if (!rtv || !rtv->is_track()) {
846 /* ignore non-tracks early on. we can't move any regions on them */
847 } else if (_last_pointer_time_axis_view < 0) {
848 /* Was in the drop-zone, now over a track.
849 * Hence it must be an upward move (from the bottom)
851 * track_index is still -1, so delta must be set to
852 * move up the correct number of tracks from the bottom.
854 * This is necessary because steps may be skipped if
855 * the bottom-most track is not a valid target and/or
856 * if there are hidden tracks at the bottom.
857 * Hence the initial offset (_ddropzone) as well as the
858 * last valid pointer position (_pdropzone) need to be
859 * taken into account.
861 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
863 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
866 /* TODO needs adjustment per DraggingView,
868 * e.g. select one region on the top-layer of a track
869 * and one region which is at the bottom-layer of another track
872 * Indicated drop-zones and layering is wrong.
873 * and may infer additional layers on the target-track
874 * (depending how many layers the original track had).
876 * Or select two regions (different layers) on a same track,
877 * move across a non-layer track.. -> layering info is lost.
878 * on drop either of the regions may be on top.
880 * Proposed solution: screw it :) well,
881 * don't use delta_layer, use an absolute value
882 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
883 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
884 * 3) iterate over all DraggingView, find the one that is over the track with most layers
885 * 4) proportionally scale layer to layers available on target
887 delta_layer = current_pointer_layer - _last_pointer_layer;
890 /* for automation lanes, there is a TimeAxisView but no ->view()
891 * if (!tv) -> dropzone
893 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
894 /* Moving into the drop-zone.. */
895 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
896 /* delta_time_axis_view may not be sufficient to move into the DZ
897 * the mouse may enter it, but it may not be a valid move due to
900 * -> remember the delta needed to move into the dropzone
902 _ddropzone = delta_time_axis_view;
903 /* ..but subtract hidden tracks (or routes) at the bottom.
904 * we silently move mover them
906 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
907 - _time_axis_views.size();
909 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
910 /* move around inside the zone.
911 * This allows to move further down until all regions are in the zone.
913 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
914 assert(ptr_y >= last_track_bottom_edge);
915 assert(_ddropzone > 0);
917 /* calculate mouse position in 'tracks' below last track. */
918 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
919 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
921 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
923 delta_time_axis_view = dzpos - _pdropzone;
924 } else if (dzpos < _pdropzone && _ndropzone > 0) {
925 // move up inside the DZ
926 delta_time_axis_view = dzpos - _pdropzone;
930 /* Work out the change in x */
931 framepos_t pending_region_position;
932 double const x_delta = compute_x_delta (event, &pending_region_position);
933 _last_frame_position = pending_region_position;
935 /* calculate hidden tracks in current y-axis delta */
937 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
938 /* The mouse is more than one track below the dropzone.
939 * distance calculation is not needed (and would not work, either
940 * because the dropzone is "packed").
942 * Except when [partially] moving regions out of dropzone in a large step.
943 * (the mouse may or may not remain in the DZ)
944 * Hidden tracks at the bottom of the TAV need to be skipped.
946 * This also handles the case if the mouse entered the DZ
947 * in a large step (exessive delta), either due to fast-movement,
948 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
950 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
951 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
953 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
954 -_time_axis_views.size() - dt;
957 else if (_last_pointer_time_axis_view < 0) {
958 /* Moving out of the zone. Check for hidden tracks at the bottom. */
959 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
960 -_time_axis_views.size() - delta_time_axis_view;
962 /* calculate hidden tracks that are skipped by the pointer movement */
963 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
964 - _last_pointer_time_axis_view
965 - delta_time_axis_view;
968 /* Verify change in y */
969 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
970 /* this y movement is not allowed, so do no y movement this time */
971 delta_time_axis_view = 0;
976 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
977 /* haven't reached next snap point, and we're not switching
978 trackviews nor layers. nothing to do.
983 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
984 PlaylistDropzoneMap playlist_dropzone_map;
985 _ndropzone = 0; // number of elements currently in the dropzone
988 /* sort views by time_axis.
989 * This retains track order in the dropzone, regardless
990 * of actual selection order
992 _views.sort (DraggingViewSorter());
994 /* count number of distinct tracks of all regions
995 * being dragged, used for dropzone.
998 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
999 if (i->time_axis_view != prev_track) {
1000 prev_track = i->time_axis_view;
1006 _views.back().time_axis_view -
1007 _views.front().time_axis_view;
1009 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1010 - _views.back().time_axis_view;
1012 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1016 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1018 RegionView* rv = i->view;
1023 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1030 /* reparent the regionview into a group above all
1034 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1035 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1036 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1037 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1038 /* move the item so that it continues to appear at the
1039 same location now that its parent has changed.
1041 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1044 /* If we have moved tracks, we'll fudge the layer delta so that the
1045 region gets moved back onto layer 0 on its new track; this avoids
1046 confusion when dragging regions from non-zero layers onto different
1049 double this_delta_layer = delta_layer;
1050 if (delta_time_axis_view != 0) {
1051 this_delta_layer = - i->layer;
1054 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1056 int track_index = i->time_axis_view + this_delta_time_axis_view;
1057 assert(track_index >= 0);
1059 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1060 /* Track is in the Dropzone */
1062 i->time_axis_view = track_index;
1063 assert(i->time_axis_view >= (int) _time_axis_views.size());
1066 double yposition = 0;
1067 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1068 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1071 /* store index of each new playlist as a negative count, starting at -1 */
1073 if (pdz == playlist_dropzone_map.end()) {
1074 /* compute where this new track (which doesn't exist yet) will live
1077 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1079 /* How high is this region view ? */
1081 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1082 ArdourCanvas::Rect bbox;
1085 bbox = obbox.get ();
1088 last_track_bottom_edge += bbox.height();
1090 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1093 yposition = pdz->second;
1096 /* values are zero or negative, hence the use of min() */
1097 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1102 /* The TimeAxisView that this region is now over */
1103 TimeAxisView* current_tv = _time_axis_views[track_index];
1105 /* Ensure it is moved from stacked -> expanded if appropriate */
1106 if (current_tv->view()->layer_display() == Stacked) {
1107 current_tv->view()->set_layer_display (Expanded);
1110 /* We're only allowed to go -ve in layer on Expanded views */
1111 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1112 this_delta_layer = - i->layer;
1116 rv->set_height (current_tv->view()->child_height ());
1118 /* Update show/hidden status as the region view may have come from a hidden track,
1119 or have moved to one.
1121 if (current_tv->hidden ()) {
1122 rv->get_canvas_group()->hide ();
1124 rv->get_canvas_group()->show ();
1127 /* Update the DraggingView */
1128 i->time_axis_view = track_index;
1129 i->layer += this_delta_layer;
1132 _editor->mouse_brush_insert_region (rv, pending_region_position);
1136 /* Get the y coordinate of the top of the track that this region is now over */
1137 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1139 /* And adjust for the layer that it should be on */
1140 StreamView* cv = current_tv->view ();
1141 switch (cv->layer_display ()) {
1145 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1148 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1152 /* need to get the parent of the regionview
1153 * canvas group and get its position in
1154 * equivalent coordinate space as the trackview
1155 * we are now dragging over.
1158 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1163 /* Now move the region view */
1164 rv->move (x_delta, y_delta);
1166 } /* foreach region */
1168 _total_x_delta += x_delta;
1170 if (x_delta != 0 && !_brushing) {
1171 show_verbose_cursor_time (_last_frame_position);
1174 /* keep track of pointer movement */
1176 /* the pointer is currently over a time axis view */
1178 if (_last_pointer_time_axis_view < 0) {
1179 /* last motion event was not over a time axis view
1180 * or last y-movement out of the dropzone was not valid
1183 if (delta_time_axis_view < 0) {
1184 /* in the drop zone, moving up */
1186 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1187 * We do not use negative _last_pointer_time_axis_view because
1188 * the dropzone is "packed" (the actual track offset is ignored)
1190 * As opposed to the actual number
1191 * of elements in the dropzone (_ndropzone)
1192 * _pdropzone is not constrained. This is necessary
1193 * to allow moving multiple regions with y-distance
1196 * There can be 0 elements in the dropzone,
1197 * even though the drag-pointer is inside the DZ.
1200 * [ Audio-track, Midi-track, Audio-track, DZ ]
1201 * move regions from both audio tracks at the same time into the
1202 * DZ by grabbing the region in the bottom track.
1204 assert(current_pointer_time_axis_view >= 0);
1205 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1209 /* only move out of the zone if the movement is OK */
1210 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1211 assert(delta_time_axis_view < 0);
1212 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1213 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1214 * the current position can be calculated as follows:
1216 // a well placed oofus attack can still throw this off.
1217 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1218 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1221 /* last motion event was also over a time axis view */
1222 _last_pointer_time_axis_view += delta_time_axis_view;
1223 assert(_last_pointer_time_axis_view >= 0);
1228 /* the pointer is not over a time axis view */
1229 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1230 _pdropzone += delta_time_axis_view - delta_skip;
1231 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1234 _last_pointer_layer += delta_layer;
1238 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1240 if (_copy && first_move) {
1241 if (_x_constrained && !_brushing) {
1242 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1243 } else if (!_brushing) {
1244 _editor->begin_reversible_command (Operations::region_copy);
1245 } else if (_brushing) {
1246 _editor->begin_reversible_command (Operations::drag_region_brush);
1248 /* duplicate the regionview(s) and region(s) */
1250 list<DraggingView> new_regionviews;
1252 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1254 RegionView* rv = i->view;
1255 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1256 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1258 const boost::shared_ptr<const Region> original = rv->region();
1259 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1260 region_copy->set_position (original->position());
1261 /* need to set this so that the drop zone code can work. This doesn't
1262 actually put the region into the playlist, but just sets a weak pointer
1265 region_copy->set_playlist (original->playlist());
1269 boost::shared_ptr<AudioRegion> audioregion_copy
1270 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1272 nrv = new AudioRegionView (*arv, audioregion_copy);
1274 boost::shared_ptr<MidiRegion> midiregion_copy
1275 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1276 nrv = new MidiRegionView (*mrv, midiregion_copy);
1281 nrv->get_canvas_group()->show ();
1282 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1284 /* swap _primary to the copy */
1286 if (rv == _primary) {
1290 /* ..and deselect the one we copied */
1292 rv->set_selected (false);
1295 if (!new_regionviews.empty()) {
1297 /* reflect the fact that we are dragging the copies */
1299 _views = new_regionviews;
1301 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1304 } else if (!_copy && first_move) {
1305 if (_x_constrained && !_brushing) {
1306 _editor->begin_reversible_command (_("fixed time region drag"));
1307 } else if (!_brushing) {
1308 _editor->begin_reversible_command (Operations::region_drag);
1309 } else if (_brushing) {
1310 _editor->begin_reversible_command (Operations::drag_region_brush);
1313 RegionMotionDrag::motion (event, first_move);
1317 RegionMotionDrag::finished (GdkEvent *, bool)
1319 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1320 if (!(*i)->view()) {
1324 if ((*i)->view()->layer_display() == Expanded) {
1325 (*i)->view()->set_layer_display (Stacked);
1331 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1333 RegionMotionDrag::finished (ev, movement_occurred);
1335 if (!movement_occurred) {
1339 if (was_double_click() && !_views.empty()) {
1340 DraggingView dv = _views.front();
1341 dv.view->show_region_editor ();
1348 assert (!_views.empty ());
1350 /* We might have hidden region views so that they weren't visible during the drag
1351 (when they have been reparented). Now everything can be shown again, as region
1352 views are back in their track parent groups.
1354 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1355 i->view->get_canvas_group()->show ();
1358 bool const changed_position = (_last_frame_position != _primary->region()->position());
1359 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1360 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1380 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1384 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1386 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1391 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1392 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1393 uint32_t output_chan = region->n_channels();
1394 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1395 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1397 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1398 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1400 rtav->set_height (original->current_height());
1404 ChanCount one_midi_port (DataType::MIDI, 1);
1405 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1406 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1407 (ARDOUR::Plugin::PresetRecord*) 0,
1408 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1409 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1411 rtav->set_height (original->current_height());
1416 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1422 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1424 RegionSelection new_views;
1425 PlaylistSet modified_playlists;
1426 RouteTimeAxisView* new_time_axis_view = 0;
1429 /* all changes were made during motion event handlers */
1431 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1435 _editor->commit_reversible_command ();
1439 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1440 PlaylistMapping playlist_mapping;
1442 /* insert the regions into their new playlists */
1443 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1445 RouteTimeAxisView* dest_rtv = 0;
1447 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1453 if (changed_position && !_x_constrained) {
1454 where = i->view->region()->position() - drag_delta;
1456 where = i->view->region()->position();
1459 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1460 /* dragged to drop zone */
1462 PlaylistMapping::iterator pm;
1464 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1465 /* first region from this original playlist: create a new track */
1466 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1467 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1468 dest_rtv = new_time_axis_view;
1470 /* we already created a new track for regions from this playlist, use it */
1471 dest_rtv = pm->second;
1474 /* destination time axis view is the one we dragged to */
1475 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1478 if (dest_rtv != 0) {
1479 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1480 if (new_view != 0) {
1481 new_views.push_back (new_view);
1485 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1486 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1489 list<DraggingView>::const_iterator next = i;
1495 /* If we've created new regions either by copying or moving
1496 to a new track, we want to replace the old selection with the new ones
1499 if (new_views.size() > 0) {
1500 _editor->selection->set (new_views);
1503 /* write commands for the accumulated diffs for all our modified playlists */
1504 add_stateful_diff_commands_for_playlists (modified_playlists);
1506 _editor->commit_reversible_command ();
1510 RegionMoveDrag::finished_no_copy (
1511 bool const changed_position,
1512 bool const changed_tracks,
1513 framecnt_t const drag_delta
1516 RegionSelection new_views;
1517 PlaylistSet modified_playlists;
1518 PlaylistSet frozen_playlists;
1519 set<RouteTimeAxisView*> views_to_update;
1520 RouteTimeAxisView* new_time_axis_view = 0;
1522 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1523 PlaylistMapping playlist_mapping;
1525 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1527 RegionView* rv = i->view;
1528 RouteTimeAxisView* dest_rtv = 0;
1530 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1535 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1536 /* dragged to drop zone */
1538 PlaylistMapping::iterator pm;
1540 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1541 /* first region from this original playlist: create a new track */
1542 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1543 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1544 dest_rtv = new_time_axis_view;
1546 /* we already created a new track for regions from this playlist, use it */
1547 dest_rtv = pm->second;
1551 /* destination time axis view is the one we dragged to */
1552 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1557 double const dest_layer = i->layer;
1559 views_to_update.insert (dest_rtv);
1563 if (changed_position && !_x_constrained) {
1564 where = rv->region()->position() - drag_delta;
1566 where = rv->region()->position();
1569 if (changed_tracks) {
1571 /* insert into new playlist */
1573 RegionView* new_view = insert_region_into_playlist (
1574 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1577 if (new_view == 0) {
1582 new_views.push_back (new_view);
1584 /* remove from old playlist */
1586 /* the region that used to be in the old playlist is not
1587 moved to the new one - we use a copy of it. as a result,
1588 any existing editor for the region should no longer be
1591 rv->hide_region_editor();
1594 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1598 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1600 /* this movement may result in a crossfade being modified, or a layering change,
1601 so we need to get undo data from the playlist as well as the region.
1604 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1606 playlist->clear_changes ();
1609 rv->region()->clear_changes ();
1612 motion on the same track. plonk the previously reparented region
1613 back to its original canvas group (its streamview).
1614 No need to do anything for copies as they are fake regions which will be deleted.
1617 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1618 rv->get_canvas_group()->set_y_position (i->initial_y);
1621 /* just change the model */
1622 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1623 playlist->set_layer (rv->region(), dest_layer);
1626 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1628 r = frozen_playlists.insert (playlist);
1631 playlist->freeze ();
1634 rv->region()->set_position (where);
1635 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1638 if (changed_tracks) {
1640 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1641 was selected in all of them, then removing it from a playlist will have removed all
1642 trace of it from _views (i.e. there were N regions selected, we removed 1,
1643 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1644 corresponding regionview, and _views is now empty).
1646 This could have invalidated any and all iterators into _views.
1648 The heuristic we use here is: if the region selection is empty, break out of the loop
1649 here. if the region selection is not empty, then restart the loop because we know that
1650 we must have removed at least the region(view) we've just been working on as well as any
1651 that we processed on previous iterations.
1653 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1654 we can just iterate.
1658 if (_views.empty()) {
1669 /* If we've created new regions either by copying or moving
1670 to a new track, we want to replace the old selection with the new ones
1673 if (new_views.size() > 0) {
1674 _editor->selection->set (new_views);
1677 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1681 /* write commands for the accumulated diffs for all our modified playlists */
1682 add_stateful_diff_commands_for_playlists (modified_playlists);
1683 /* applies to _brushing */
1684 _editor->commit_reversible_command ();
1686 /* We have futzed with the layering of canvas items on our streamviews.
1687 If any region changed layer, this will have resulted in the stream
1688 views being asked to set up their region views, and all will be well.
1689 If not, we might now have badly-ordered region views. Ask the StreamViews
1690 involved to sort themselves out, just in case.
1693 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1694 (*i)->view()->playlist_layered ((*i)->track ());
1698 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1699 * @param region Region to remove.
1700 * @param playlist playlist To remove from.
1701 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1702 * that clear_changes () is only called once per playlist.
1705 RegionMoveDrag::remove_region_from_playlist (
1706 boost::shared_ptr<Region> region,
1707 boost::shared_ptr<Playlist> playlist,
1708 PlaylistSet& modified_playlists
1711 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1714 playlist->clear_changes ();
1717 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1721 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1722 * clearing the playlist's diff history first if necessary.
1723 * @param region Region to insert.
1724 * @param dest_rtv Destination RouteTimeAxisView.
1725 * @param dest_layer Destination layer.
1726 * @param where Destination position.
1727 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1728 * that clear_changes () is only called once per playlist.
1729 * @return New RegionView, or 0 if no insert was performed.
1732 RegionMoveDrag::insert_region_into_playlist (
1733 boost::shared_ptr<Region> region,
1734 RouteTimeAxisView* dest_rtv,
1737 PlaylistSet& modified_playlists
1740 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1741 if (!dest_playlist) {
1745 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1746 _new_region_view = 0;
1747 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1749 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1750 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1752 dest_playlist->clear_changes ();
1755 dest_playlist->add_region (region, where);
1757 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1758 dest_playlist->set_layer (region, dest_layer);
1763 assert (_new_region_view);
1765 return _new_region_view;
1769 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1771 _new_region_view = rv;
1775 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1777 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1778 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1780 _editor->session()->add_command (c);
1789 RegionMoveDrag::aborted (bool movement_occurred)
1793 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1794 list<DraggingView>::const_iterator next = i;
1803 RegionMotionDrag::aborted (movement_occurred);
1808 RegionMotionDrag::aborted (bool)
1810 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1812 StreamView* sview = (*i)->view();
1815 if (sview->layer_display() == Expanded) {
1816 sview->set_layer_display (Stacked);
1821 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1822 RegionView* rv = i->view;
1823 TimeAxisView* tv = &(rv->get_time_axis_view ());
1824 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1826 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1827 rv->get_canvas_group()->set_y_position (0);
1829 rv->move (-_total_x_delta, 0);
1830 rv->set_height (rtv->view()->child_height ());
1834 /** @param b true to brush, otherwise false.
1835 * @param c true to make copies of the regions being moved, otherwise false.
1837 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1838 : RegionMotionDrag (e, i, p, v, b)
1840 , _new_region_view (0)
1842 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1845 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1846 if (rtv && rtv->is_track()) {
1847 speed = rtv->track()->speed ();
1850 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1854 RegionMoveDrag::setup_pointer_frame_offset ()
1856 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1859 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1860 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1862 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1864 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1865 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1867 _primary = v->view()->create_region_view (r, false, false);
1869 _primary->get_canvas_group()->show ();
1870 _primary->set_position (pos, 0);
1871 _views.push_back (DraggingView (_primary, this, v));
1873 _last_frame_position = pos;
1875 _item = _primary->get_canvas_group ();
1879 RegionInsertDrag::finished (GdkEvent *, bool)
1881 int pos = _views.front().time_axis_view;
1882 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1884 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1886 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1887 _primary->get_canvas_group()->set_y_position (0);
1889 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1891 _editor->begin_reversible_command (Operations::insert_region);
1892 playlist->clear_changes ();
1893 playlist->add_region (_primary->region (), _last_frame_position);
1895 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1896 if (Config->get_edit_mode() == Ripple) {
1897 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1900 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1901 _editor->commit_reversible_command ();
1909 RegionInsertDrag::aborted (bool)
1916 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1917 : RegionMoveDrag (e, i, p, v, false, false)
1919 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1922 struct RegionSelectionByPosition {
1923 bool operator() (RegionView*a, RegionView* b) {
1924 return a->region()->position () < b->region()->position();
1929 RegionSpliceDrag::motion (GdkEvent* event, bool)
1931 /* Which trackview is this ? */
1933 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1934 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1936 /* The region motion is only processed if the pointer is over
1940 if (!tv || !tv->is_track()) {
1941 /* To make sure we hide the verbose canvas cursor when the mouse is
1942 not held over an audio track.
1944 _editor->verbose_cursor()->hide ();
1947 _editor->verbose_cursor()->show ();
1952 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1958 RegionSelection copy;
1959 _editor->selection->regions.by_position(copy);
1961 framepos_t const pf = adjusted_current_frame (event);
1963 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1965 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1971 boost::shared_ptr<Playlist> playlist;
1973 if ((playlist = atv->playlist()) == 0) {
1977 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1982 if (pf < (*i)->region()->last_frame() + 1) {
1986 if (pf > (*i)->region()->first_frame()) {
1992 playlist->shuffle ((*i)->region(), dir);
1997 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1999 RegionMoveDrag::finished (event, movement_occurred);
2003 RegionSpliceDrag::aborted (bool)
2013 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2016 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2018 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2019 RegionSelection to_ripple;
2020 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2021 if ((*i)->position() >= where) {
2022 to_ripple.push_back (rtv->view()->find_view(*i));
2026 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2027 if (!exclude.contains (*i)) {
2028 // the selection has already been added to _views
2030 if (drag_in_progress) {
2031 // do the same things that RegionMotionDrag::motion does when
2032 // first_move is true, for the region views that we're adding
2033 // to _views this time
2036 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2037 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2038 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2039 rvg->reparent (_editor->_drag_motion_group);
2041 // we only need to move in the y direction
2042 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2047 _views.push_back (DraggingView (*i, this, tav));
2053 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2056 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2057 // we added all the regions after the selection
2059 std::list<DraggingView>::iterator to_erase = i++;
2060 if (!_editor->selection->regions.contains (to_erase->view)) {
2061 // restore the non-selected regions to their original playlist & positions,
2062 // and then ripple them back by the length of the regions that were dragged away
2063 // do the same things as RegionMotionDrag::aborted
2065 RegionView *rv = to_erase->view;
2066 TimeAxisView* tv = &(rv->get_time_axis_view ());
2067 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2070 // plonk them back onto their own track
2071 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2072 rv->get_canvas_group()->set_y_position (0);
2076 // move the underlying region to match the view
2077 rv->region()->set_position (rv->region()->position() + amount);
2079 // restore the view to match the underlying region's original position
2080 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2083 rv->set_height (rtv->view()->child_height ());
2084 _views.erase (to_erase);
2090 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2092 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2094 return allow_moves_across_tracks;
2102 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2103 : RegionMoveDrag (e, i, p, v, false, false)
2105 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2106 // compute length of selection
2107 RegionSelection selected_regions = _editor->selection->regions;
2108 selection_length = selected_regions.end_frame() - selected_regions.start();
2110 // we'll only allow dragging to another track in ripple mode if all the regions
2111 // being dragged start off on the same track
2112 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2115 exclude = new RegionList;
2116 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2117 exclude->push_back((*i)->region());
2120 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2121 RegionSelection copy;
2122 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2124 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2125 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2127 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2128 // find ripple start point on each applicable playlist
2129 RegionView *first_selected_on_this_track = NULL;
2130 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2131 if ((*i)->region()->playlist() == (*pi)) {
2132 // region is on this playlist - it's the first, because they're sorted
2133 first_selected_on_this_track = *i;
2137 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2138 add_all_after_to_views (
2139 &first_selected_on_this_track->get_time_axis_view(),
2140 first_selected_on_this_track->region()->position(),
2141 selected_regions, false);
2144 if (allow_moves_across_tracks) {
2145 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2153 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2155 /* Which trackview is this ? */
2157 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2158 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2160 /* The region motion is only processed if the pointer is over
2164 if (!tv || !tv->is_track()) {
2165 /* To make sure we hide the verbose canvas cursor when the mouse is
2166 not held over an audiotrack.
2168 _editor->verbose_cursor()->hide ();
2172 framepos_t where = adjusted_current_frame (event);
2173 assert (where >= 0);
2175 double delta = compute_x_delta (event, &after);
2177 framecnt_t amount = _editor->pixel_to_sample (delta);
2179 if (allow_moves_across_tracks) {
2180 // all the originally selected regions were on the same track
2182 framecnt_t adjust = 0;
2183 if (prev_tav && tv != prev_tav) {
2184 // dragged onto a different track
2185 // remove the unselected regions from _views, restore them to their original positions
2186 // and add the regions after the drop point on the new playlist to _views instead.
2187 // undo the effect of rippling the previous playlist, and include the effect of removing
2188 // the dragged region(s) from this track
2190 remove_unselected_from_views (prev_amount, false);
2191 // ripple previous playlist according to the regions that have been removed onto the new playlist
2192 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2195 // move just the selected regions
2196 RegionMoveDrag::motion(event, first_move);
2198 // ensure that the ripple operation on the new playlist inserts selection_length time
2199 adjust = selection_length;
2200 // ripple the new current playlist
2201 tv->playlist()->ripple (where, amount+adjust, exclude);
2203 // add regions after point where drag entered this track to subsequent ripples
2204 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2207 // motion on same track
2208 RegionMoveDrag::motion(event, first_move);
2212 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2213 prev_position = where;
2215 // selection encompasses multiple tracks - just drag
2216 // cross-track drags are forbidden
2217 RegionMoveDrag::motion(event, first_move);
2220 if (!_x_constrained) {
2221 prev_amount += amount;
2224 _last_frame_position = after;
2228 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2230 if (!movement_occurred) {
2234 if (was_double_click() && !_views.empty()) {
2235 DraggingView dv = _views.front();
2236 dv.view->show_region_editor ();
2243 _editor->begin_reversible_command(_("Ripple drag"));
2245 // remove the regions being rippled from the dragging view, updating them to
2246 // their new positions
2247 remove_unselected_from_views (prev_amount, true);
2249 if (allow_moves_across_tracks) {
2251 // if regions were dragged across tracks, we've rippled any later
2252 // regions on the track the regions were dragged off, so we need
2253 // to add the original track to the undo record
2254 orig_tav->playlist()->clear_changes();
2255 vector<Command*> cmds;
2256 orig_tav->playlist()->rdiff (cmds);
2257 _editor->session()->add_commands (cmds);
2259 if (prev_tav && prev_tav != orig_tav) {
2260 prev_tav->playlist()->clear_changes();
2261 vector<Command*> cmds;
2262 prev_tav->playlist()->rdiff (cmds);
2263 _editor->session()->add_commands (cmds);
2266 // selection spanned multiple tracks - all will need adding to undo record
2268 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2269 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2271 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2272 (*pi)->clear_changes();
2273 vector<Command*> cmds;
2274 (*pi)->rdiff (cmds);
2275 _editor->session()->add_commands (cmds);
2279 // other modified playlists are added to undo by RegionMoveDrag::finished()
2280 RegionMoveDrag::finished (event, movement_occurred);
2281 _editor->commit_reversible_command();
2285 RegionRippleDrag::aborted (bool movement_occurred)
2287 RegionMoveDrag::aborted (movement_occurred);
2292 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2294 _view (dynamic_cast<MidiTimeAxisView*> (v))
2296 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2302 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2305 _editor->begin_reversible_command (_("create region"));
2306 _region = add_midi_region (_view, false);
2307 _view->playlist()->freeze ();
2310 framepos_t const f = adjusted_current_frame (event);
2311 if (f < grab_frame()) {
2312 _region->set_initial_position (f);
2315 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2316 so that if this region is duplicated, its duplicate starts on
2317 a snap point rather than 1 frame after a snap point. Otherwise things get
2318 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2319 place snapped notes at the start of the region.
2322 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2323 _region->set_length (len < 1 ? 1 : len);
2329 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2331 if (!movement_occurred) {
2332 add_midi_region (_view, true);
2334 _view->playlist()->thaw ();
2335 _editor->commit_reversible_command();
2340 RegionCreateDrag::aborted (bool)
2343 _view->playlist()->thaw ();
2349 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2354 , _was_selected (false)
2357 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2361 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2363 Gdk::Cursor* cursor;
2364 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2366 float x_fraction = cnote->mouse_x_fraction ();
2368 if (x_fraction > 0.0 && x_fraction < 0.25) {
2369 cursor = _editor->cursors()->left_side_trim;
2372 cursor = _editor->cursors()->right_side_trim;
2376 Drag::start_grab (event, cursor);
2378 region = &cnote->region_view();
2381 temp = region->snap_to_pixel (cnote->x0 (), true);
2382 _snap_delta = temp - cnote->x0 ();
2386 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2391 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2392 if (ms.size() > 1) {
2393 /* has to be relative, may make no sense otherwise */
2397 if (!(_was_selected = cnote->selected())) {
2399 /* tertiary-click means extend selection - we'll do that on button release,
2400 so don't add it here, because otherwise we make it hard to figure
2401 out the "extend-to" range.
2404 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2407 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2410 region->note_selected (cnote, true);
2412 _editor->get_selection().clear_points();
2413 region->unique_select (cnote);
2420 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2422 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2424 _editor->begin_reversible_command (_("resize notes"));
2426 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2427 MidiRegionSelection::iterator next;
2430 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2432 mrv->begin_resizing (at_front);
2438 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2439 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2441 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2445 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2447 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2448 if (_editor->snap_mode () != SnapOff) {
2452 if (_editor->snap_mode () == SnapOff) {
2454 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2455 if (apply_snap_delta) {
2461 if (apply_snap_delta) {
2465 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2471 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2473 if (!movement_occurred) {
2474 /* no motion - select note */
2475 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2476 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2477 _editor->current_mouse_mode() == Editing::MouseDraw) {
2479 bool changed = false;
2481 if (_was_selected) {
2482 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2484 region->note_deselected (cnote);
2487 _editor->get_selection().clear_points();
2488 region->unique_select (cnote);
2492 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2493 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2495 if (!extend && !add && region->selection_size() > 1) {
2496 _editor->get_selection().clear_points();
2497 region->unique_select (cnote);
2499 } else if (extend) {
2500 region->note_selected (cnote, true, true);
2503 /* it was added during button press */
2509 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2510 _editor->commit_reversible_selection_op();
2517 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2518 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2519 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2521 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2524 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2526 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2527 if (_editor->snap_mode () != SnapOff) {
2531 if (_editor->snap_mode () == SnapOff) {
2533 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2534 if (apply_snap_delta) {
2540 if (apply_snap_delta) {
2544 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2548 _editor->commit_reversible_command ();
2552 NoteResizeDrag::aborted (bool)
2554 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2555 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2556 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2558 mrv->abort_resizing ();
2563 AVDraggingView::AVDraggingView (RegionView* v)
2566 initial_position = v->region()->position ();
2569 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2572 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2575 TrackViewList empty;
2577 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2578 std::list<RegionView*> views = rs.by_layer();
2581 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2582 RegionView* rv = (*i);
2583 if (!rv->region()->video_locked()) {
2586 if (rv->region()->locked()) {
2589 _views.push_back (AVDraggingView (rv));
2594 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2596 Drag::start_grab (event);
2597 if (_editor->session() == 0) {
2601 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2607 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2611 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2612 _max_backwards_drag = (
2613 ARDOUR_UI::instance()->video_timeline->get_duration()
2614 + ARDOUR_UI::instance()->video_timeline->get_offset()
2615 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2618 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2619 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2620 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2623 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2626 Timecode::Time timecode;
2627 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2628 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);
2629 show_verbose_cursor_text (buf);
2633 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2635 if (_editor->session() == 0) {
2638 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2642 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2646 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2647 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2649 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2650 dt = - _max_backwards_drag;
2653 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2654 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2656 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2657 RegionView* rv = i->view;
2658 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2661 rv->region()->clear_changes ();
2662 rv->region()->suspend_property_changes();
2664 rv->region()->set_position(i->initial_position + dt);
2665 rv->region_changed(ARDOUR::Properties::position);
2668 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2669 Timecode::Time timecode;
2670 Timecode::Time timediff;
2672 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2673 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2674 snprintf (buf, sizeof (buf),
2675 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2676 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2677 , _("Video Start:"),
2678 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2680 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2682 show_verbose_cursor_text (buf);
2686 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2688 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2695 if (!movement_occurred || ! _editor->session()) {
2699 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2701 _editor->begin_reversible_command (_("Move Video"));
2703 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2704 ARDOUR_UI::instance()->video_timeline->save_undo();
2705 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2706 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2708 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2709 i->view->drag_end();
2710 i->view->region()->resume_property_changes ();
2712 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2715 _editor->session()->maybe_update_session_range(
2716 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2717 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2721 _editor->commit_reversible_command ();
2725 VideoTimeLineDrag::aborted (bool)
2727 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2730 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2731 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2733 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2734 i->view->region()->resume_property_changes ();
2735 i->view->region()->set_position(i->initial_position);
2739 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2740 : RegionDrag (e, i, p, v)
2741 , _operation (StartTrim)
2742 , _preserve_fade_anchor (preserve_fade_anchor)
2743 , _jump_position_when_done (false)
2745 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2749 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2752 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2753 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2755 if (tv && tv->is_track()) {
2756 speed = tv->track()->speed();
2759 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2760 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2761 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2763 framepos_t const pf = adjusted_current_frame (event);
2764 setup_snap_delta (region_start);
2766 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2767 /* Move the contents of the region around without changing the region bounds */
2768 _operation = ContentsTrim;
2769 Drag::start_grab (event, _editor->cursors()->trimmer);
2771 /* These will get overridden for a point trim.*/
2772 if (pf < (region_start + region_length/2)) {
2773 /* closer to front */
2774 _operation = StartTrim;
2775 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2776 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2778 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2782 _operation = EndTrim;
2783 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2784 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2786 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2790 /* jump trim disabled for now
2791 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2792 _jump_position_when_done = true;
2796 switch (_operation) {
2798 show_verbose_cursor_time (region_start);
2801 show_verbose_cursor_duration (region_start, region_end);
2804 show_verbose_cursor_time (pf);
2808 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2809 i->view->region()->suspend_property_changes ();
2814 TrimDrag::motion (GdkEvent* event, bool first_move)
2816 RegionView* rv = _primary;
2819 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2820 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2821 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2822 frameoffset_t frame_delta = 0;
2824 if (tv && tv->is_track()) {
2825 speed = tv->track()->speed();
2827 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2828 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2834 switch (_operation) {
2836 trim_type = "Region start trim";
2839 trim_type = "Region end trim";
2842 trim_type = "Region content trim";
2849 _editor->begin_reversible_command (trim_type);
2851 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2852 RegionView* rv = i->view;
2853 rv->enable_display (false);
2854 rv->region()->playlist()->clear_owned_changes ();
2856 if (_operation == StartTrim) {
2857 rv->trim_front_starting ();
2860 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2863 arv->temporarily_hide_envelope ();
2867 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2868 insert_result = _editor->motion_frozen_playlists.insert (pl);
2870 if (insert_result.second) {
2876 bool non_overlap_trim = false;
2878 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2879 non_overlap_trim = true;
2882 /* contstrain trim to fade length */
2883 if (_preserve_fade_anchor) {
2884 switch (_operation) {
2886 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2887 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2889 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2890 if (ar->locked()) continue;
2891 framecnt_t len = ar->fade_in()->back()->when;
2892 if (len < dt) dt = min(dt, len);
2896 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2897 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2899 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2900 if (ar->locked()) continue;
2901 framecnt_t len = ar->fade_out()->back()->when;
2902 if (len < -dt) dt = max(dt, -len);
2911 switch (_operation) {
2913 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2914 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2915 if (changed && _preserve_fade_anchor) {
2916 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2918 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2919 framecnt_t len = ar->fade_in()->back()->when;
2920 framecnt_t diff = ar->first_frame() - i->initial_position;
2921 framepos_t new_length = len - diff;
2922 i->anchored_fade_length = min (ar->length(), new_length);
2923 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2924 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2931 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2932 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2933 if (changed && _preserve_fade_anchor) {
2934 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2936 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2937 framecnt_t len = ar->fade_out()->back()->when;
2938 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2939 framepos_t new_length = len + diff;
2940 i->anchored_fade_length = min (ar->length(), new_length);
2941 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2942 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2950 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2952 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2953 i->view->move_contents (frame_delta);
2959 switch (_operation) {
2961 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2964 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2967 // show_verbose_cursor_time (frame_delta);
2973 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2975 if (movement_occurred) {
2976 motion (event, false);
2978 if (_operation == StartTrim) {
2979 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2981 /* This must happen before the region's StatefulDiffCommand is created, as it may
2982 `correct' (ahem) the region's _start from being negative to being zero. It
2983 needs to be zero in the undo record.
2985 i->view->trim_front_ending ();
2987 if (_preserve_fade_anchor) {
2988 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2990 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2991 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2992 ar->set_fade_in_length(i->anchored_fade_length);
2993 ar->set_fade_in_active(true);
2996 if (_jump_position_when_done) {
2997 i->view->region()->set_position (i->initial_position);
3000 } else if (_operation == EndTrim) {
3001 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3002 if (_preserve_fade_anchor) {
3003 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3005 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3006 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3007 ar->set_fade_out_length(i->anchored_fade_length);
3008 ar->set_fade_out_active(true);
3011 if (_jump_position_when_done) {
3012 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3017 if (!_views.empty()) {
3018 if (_operation == StartTrim) {
3019 _editor->maybe_locate_with_edit_preroll(
3020 _views.begin()->view->region()->position());
3022 if (_operation == EndTrim) {
3023 _editor->maybe_locate_with_edit_preroll(
3024 _views.begin()->view->region()->position() +
3025 _views.begin()->view->region()->length());
3029 if (!_editor->selection->selected (_primary)) {
3030 _primary->thaw_after_trim ();
3033 set<boost::shared_ptr<Playlist> > diffed_playlists;
3035 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3036 i->view->thaw_after_trim ();
3037 i->view->enable_display (true);
3039 /* Trimming one region may affect others on the playlist, so we need
3040 to get undo Commands from the whole playlist rather than just the
3041 region. Use diffed_playlists to make sure we don't diff a given
3042 playlist more than once.
3044 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3045 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3046 vector<Command*> cmds;
3048 _editor->session()->add_commands (cmds);
3049 diffed_playlists.insert (p);
3054 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3058 _editor->motion_frozen_playlists.clear ();
3059 _editor->commit_reversible_command();
3062 /* no mouse movement */
3063 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3064 _editor->point_trim (event, adjusted_current_frame (event));
3068 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3069 i->view->region()->resume_property_changes ();
3074 TrimDrag::aborted (bool movement_occurred)
3076 /* Our motion method is changing model state, so use the Undo system
3077 to cancel. Perhaps not ideal, as this will leave an Undo point
3078 behind which may be slightly odd from the user's point of view.
3083 if (movement_occurred) {
3087 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3088 i->view->region()->resume_property_changes ();
3093 TrimDrag::setup_pointer_frame_offset ()
3095 list<DraggingView>::iterator i = _views.begin ();
3096 while (i != _views.end() && i->view != _primary) {
3100 if (i == _views.end()) {
3104 switch (_operation) {
3106 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3109 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3116 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3119 , _old_snap_type (e->snap_type())
3120 , _old_snap_mode (e->snap_mode())
3123 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3124 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3126 _real_section = &_marker->meter();
3131 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3133 Drag::start_grab (event, cursor);
3134 show_verbose_cursor_time (adjusted_current_frame(event));
3138 MeterMarkerDrag::setup_pointer_frame_offset ()
3140 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3144 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3147 // create a dummy marker to catch events, then hide it.
3150 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3152 _marker = new MeterMarker (
3154 *_editor->meter_group,
3155 UIConfiguration::instance().color ("meter marker"),
3157 *new MeterSection (_marker->meter())
3160 /* use the new marker for the grab */
3161 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3164 TempoMap& map (_editor->session()->tempo_map());
3165 /* get current state */
3166 before_state = &map.get_state();
3169 _editor->begin_reversible_command (_("move meter mark"));
3171 _editor->begin_reversible_command (_("copy meter mark"));
3173 Timecode::BBT_Time bbt = _real_section->bbt();
3175 /* we can't add a meter where one currently exists */
3176 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3181 const double beat = map.beat_at_bbt (bbt);
3182 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3183 , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style());
3186 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3187 if (_real_section->position_lock_style() != AudioTime) {
3188 _editor->set_snap_to (SnapToBar);
3189 _editor->set_snap_mode (SnapNormal);
3193 framepos_t pf = adjusted_current_frame (event);
3195 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3196 /* never snap to music for audio locked */
3197 pf = adjusted_current_frame (event, false);
3200 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3202 /* fake marker meeds to stay under the mouse, unlike the real one. */
3203 _marker->set_position (adjusted_current_frame (event, false));
3205 show_verbose_cursor_time (_real_section->frame());
3209 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3211 if (!movement_occurred) {
3212 if (was_double_click()) {
3213 _editor->edit_meter_marker (*_marker);
3218 /* reinstate old snap setting */
3219 _editor->set_snap_to (_old_snap_type);
3220 _editor->set_snap_mode (_old_snap_mode);
3222 TempoMap& map (_editor->session()->tempo_map());
3224 XMLNode &after = map.get_state();
3225 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3226 _editor->commit_reversible_command ();
3228 // delete the dummy marker we used for visual representation while moving.
3229 // a new visual marker will show up automatically.
3234 MeterMarkerDrag::aborted (bool moved)
3236 _marker->set_position (_marker->meter().frame ());
3238 /* reinstate old snap setting */
3239 _editor->set_snap_to (_old_snap_type);
3240 _editor->set_snap_mode (_old_snap_mode);
3242 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3243 // delete the dummy marker we used for visual representation while moving.
3244 // a new visual marker will show up automatically.
3249 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3254 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3256 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3257 _real_section = &_marker->tempo();
3258 _movable = _real_section->movable();
3263 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3265 Drag::start_grab (event, cursor);
3266 if (!_real_section->active()) {
3267 show_verbose_cursor_text (_("inactive"));
3269 show_verbose_cursor_time (adjusted_current_frame (event));
3274 TempoMarkerDrag::setup_pointer_frame_offset ()
3276 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3280 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3282 if (!_real_section->active()) {
3288 // mvc drag - create a dummy marker to catch events, hide it.
3291 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3293 TempoSection section (_marker->tempo());
3295 _marker = new TempoMarker (
3297 *_editor->tempo_group,
3298 UIConfiguration::instance().color ("tempo marker"),
3300 *new TempoSection (_marker->tempo())
3303 /* use the new marker for the grab */
3304 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3307 TempoMap& map (_editor->session()->tempo_map());
3308 /* get current state */
3309 before_state = &map.get_state();
3312 _editor->begin_reversible_command (_("move tempo mark"));
3315 const framepos_t frame = adjusted_current_frame (event) + 1;
3317 _editor->begin_reversible_command (_("copy tempo mark"));
3319 if (_real_section->position_lock_style() == MusicTime) {
3320 _real_section = map.add_tempo (_marker->tempo(), map.pulse_at_frame (frame), 0, _real_section->type(), MusicTime);
3322 _real_section = map.add_tempo (_marker->tempo(), 0.0, frame, _real_section->type(), AudioTime);
3328 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3329 /* use vertical movement to alter tempo .. should be log */
3330 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3333 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3335 show_verbose_cursor_text (strs.str());
3337 } else if (_movable && !_real_section->locked_to_meter()) {
3338 TempoMap& map (_editor->session()->tempo_map());
3339 const bool was_music = _real_section->position_lock_style() == MusicTime;
3341 const framepos_t pf = adjusted_current_frame (event);
3343 /* cop-out : we really should set the musical position of music-locked tempo sections here, which generally works well.
3344 The problem is that when the mouse pointer's motion causes the location of the beat to change in a more-or-less
3345 chaotic way when cross-dragging tempo sections (the beat the pointer is now above could have changed without any pointer motion).
3346 Until there is a way to deal with this, just pretend the tempo section is audio-locked.
3349 _real_section->set_position_lock_style (AudioTime);
3352 map.gui_move_tempo (_real_section, pf);
3355 _real_section->set_position_lock_style (MusicTime);
3358 show_verbose_cursor_time (_real_section->frame());
3361 _marker->set_position (adjusted_current_frame (event, false));
3365 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3367 if (!_real_section->active()) {
3370 if (!movement_occurred) {
3371 if (was_double_click()) {
3372 _editor->edit_tempo_marker (*_marker);
3377 TempoMap& map (_editor->session()->tempo_map());
3379 XMLNode &after = map.get_state();
3380 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3381 _editor->commit_reversible_command ();
3383 // delete the dummy marker we used for visual representation while moving.
3384 // a new visual marker will show up automatically.
3389 TempoMarkerDrag::aborted (bool moved)
3391 _marker->set_position (_marker->tempo().frame());
3393 TempoMap& map (_editor->session()->tempo_map());
3394 map.set_state (*before_state, Stateful::current_state_version);
3395 // delete the dummy marker we used for visual representation while moving.
3396 // a new visual marker will show up automatically.
3401 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3407 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3412 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3414 Drag::start_grab (event, cursor);
3415 TempoMap& map (_editor->session()->tempo_map());
3416 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3419 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3420 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3421 show_verbose_cursor_text (sstr.str());
3422 finished (event, false);
3426 BBTRulerDrag::setup_pointer_frame_offset ()
3428 TempoMap& map (_editor->session()->tempo_map());
3429 const double beat_at_frame = map.beat_at_frame (raw_grab_frame());
3430 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3433 if (divisions > 0) {
3434 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3436 /* while it makes some sense for the user to determine the division to 'grab',
3437 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3438 and the result over steep tempo curves. Use sixteenths.
3440 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3443 _pulse = map.pulse_at_beat (beat);
3445 _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3450 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3452 TempoMap& map (_editor->session()->tempo_map());
3455 /* get current state */
3456 before_state = &map.get_state();
3457 _editor->begin_reversible_command (_("dilate tempo"));
3462 if (_editor->snap_musical()) {
3463 pf = adjusted_current_frame (event, false);
3465 pf = adjusted_current_frame (event);
3468 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3469 /* adjust previous tempo to match pointer frame */
3470 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3473 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3474 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3475 show_verbose_cursor_text (sstr.str());
3479 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3481 if (!movement_occurred) {
3485 TempoMap& map (_editor->session()->tempo_map());
3487 XMLNode &after = map.get_state();
3488 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3489 _editor->commit_reversible_command ();
3493 BBTRulerDrag::aborted (bool moved)
3496 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3501 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3502 : Drag (e, &c.track_canvas_item(), false)
3507 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3510 /** Do all the things we do when dragging the playhead to make it look as though
3511 * we have located, without actually doing the locate (because that would cause
3512 * the diskstream buffers to be refilled, which is too slow).
3515 CursorDrag::fake_locate (framepos_t t)
3517 if (_editor->session () == 0) {
3521 _editor->playhead_cursor->set_position (t);
3523 Session* s = _editor->session ();
3524 if (s->timecode_transmission_suspended ()) {
3525 framepos_t const f = _editor->playhead_cursor->current_frame ();
3526 /* This is asynchronous so it will be sent "now"
3528 s->send_mmc_locate (f);
3529 /* These are synchronous and will be sent during the next
3532 s->queue_full_time_code ();
3533 s->queue_song_position_pointer ();
3536 show_verbose_cursor_time (t);
3537 _editor->UpdateAllTransportClocks (t);
3541 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3543 Drag::start_grab (event, c);
3544 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3546 _grab_zoom = _editor->samples_per_pixel;
3548 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3550 _editor->snap_to_with_modifier (where, event);
3552 _editor->_dragging_playhead = true;
3554 Session* s = _editor->session ();
3556 /* grab the track canvas item as well */
3558 _cursor.track_canvas_item().grab();
3561 if (_was_rolling && _stop) {
3565 if (s->is_auditioning()) {
3566 s->cancel_audition ();
3570 if (AudioEngine::instance()->connected()) {
3572 /* do this only if we're the engine is connected
3573 * because otherwise this request will never be
3574 * serviced and we'll busy wait forever. likewise,
3575 * notice if we are disconnected while waiting for the
3576 * request to be serviced.
3579 s->request_suspend_timecode_transmission ();
3580 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3581 /* twiddle our thumbs */
3586 fake_locate (where - snap_delta (event->button.state));
3590 CursorDrag::motion (GdkEvent* event, bool)
3592 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3593 _editor->snap_to_with_modifier (where, event);
3594 if (where != last_pointer_frame()) {
3595 fake_locate (where - snap_delta (event->button.state));
3600 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3602 _editor->_dragging_playhead = false;
3604 _cursor.track_canvas_item().ungrab();
3606 if (!movement_occurred && _stop) {
3610 motion (event, false);
3612 Session* s = _editor->session ();
3614 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3615 _editor->_pending_locate_request = true;
3616 s->request_resume_timecode_transmission ();
3621 CursorDrag::aborted (bool)
3623 _cursor.track_canvas_item().ungrab();
3625 if (_editor->_dragging_playhead) {
3626 _editor->session()->request_resume_timecode_transmission ();
3627 _editor->_dragging_playhead = false;
3630 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3633 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3634 : RegionDrag (e, i, p, v)
3636 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3640 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3642 Drag::start_grab (event, cursor);
3644 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3645 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3646 setup_snap_delta (r->position ());
3648 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3652 FadeInDrag::setup_pointer_frame_offset ()
3654 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3655 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3656 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3660 FadeInDrag::motion (GdkEvent* event, bool)
3662 framecnt_t fade_length;
3664 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3665 _editor->snap_to_with_modifier (pos, event);
3666 pos -= snap_delta (event->button.state);
3668 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3670 if (pos < (region->position() + 64)) {
3671 fade_length = 64; // this should be a minimum defined somewhere
3672 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3673 fade_length = region->length() - region->fade_out()->back()->when - 1;
3675 fade_length = pos - region->position();
3678 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3680 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3686 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3689 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3693 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3695 if (!movement_occurred) {
3699 framecnt_t fade_length;
3700 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3701 _editor->snap_to_with_modifier (pos, event);
3702 pos -= snap_delta (event->button.state);
3704 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3706 if (pos < (region->position() + 64)) {
3707 fade_length = 64; // this should be a minimum defined somewhere
3708 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3709 fade_length = region->length() - region->fade_out()->back()->when - 1;
3711 fade_length = pos - region->position();
3714 bool in_command = false;
3716 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3718 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3724 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3725 XMLNode &before = alist->get_state();
3727 tmp->audio_region()->set_fade_in_length (fade_length);
3728 tmp->audio_region()->set_fade_in_active (true);
3731 _editor->begin_reversible_command (_("change fade in length"));
3734 XMLNode &after = alist->get_state();
3735 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3739 _editor->commit_reversible_command ();
3744 FadeInDrag::aborted (bool)
3746 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3747 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3753 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3757 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3758 : RegionDrag (e, i, p, v)
3760 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3764 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3766 Drag::start_grab (event, cursor);
3768 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3769 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3770 setup_snap_delta (r->last_frame ());
3772 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3776 FadeOutDrag::setup_pointer_frame_offset ()
3778 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3779 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3780 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3784 FadeOutDrag::motion (GdkEvent* event, bool)
3786 framecnt_t fade_length;
3788 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3789 _editor->snap_to_with_modifier (pos, event);
3790 pos -= snap_delta (event->button.state);
3792 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3794 if (pos > (region->last_frame() - 64)) {
3795 fade_length = 64; // this should really be a minimum fade defined somewhere
3796 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3797 fade_length = region->length() - region->fade_in()->back()->when - 1;
3799 fade_length = region->last_frame() - pos;
3802 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3804 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3810 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3813 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3817 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3819 if (!movement_occurred) {
3823 framecnt_t fade_length;
3825 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3826 _editor->snap_to_with_modifier (pos, event);
3827 pos -= snap_delta (event->button.state);
3829 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3831 if (pos > (region->last_frame() - 64)) {
3832 fade_length = 64; // this should really be a minimum fade defined somewhere
3833 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3834 fade_length = region->length() - region->fade_in()->back()->when - 1;
3836 fade_length = region->last_frame() - pos;
3839 bool in_command = false;
3841 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3843 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3849 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3850 XMLNode &before = alist->get_state();
3852 tmp->audio_region()->set_fade_out_length (fade_length);
3853 tmp->audio_region()->set_fade_out_active (true);
3856 _editor->begin_reversible_command (_("change fade out length"));
3859 XMLNode &after = alist->get_state();
3860 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3864 _editor->commit_reversible_command ();
3869 FadeOutDrag::aborted (bool)
3871 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3872 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3878 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3882 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3884 , _selection_changed (false)
3886 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3887 Gtk::Window* toplevel = _editor->current_toplevel();
3888 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3892 _points.push_back (ArdourCanvas::Duple (0, 0));
3894 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3897 MarkerDrag::~MarkerDrag ()
3899 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3904 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3906 location = new Location (*l);
3907 markers.push_back (m);
3912 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3914 Drag::start_grab (event, cursor);
3918 Location *location = _editor->find_location_from_marker (_marker, is_start);
3919 _editor->_dragging_edit_point = true;
3921 update_item (location);
3923 // _drag_line->show();
3924 // _line->raise_to_top();
3927 show_verbose_cursor_time (location->start());
3929 show_verbose_cursor_time (location->end());
3931 setup_snap_delta (is_start ? location->start() : location->end());
3933 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3936 case Selection::Toggle:
3937 /* we toggle on the button release */
3939 case Selection::Set:
3940 if (!_editor->selection->selected (_marker)) {
3941 _editor->selection->set (_marker);
3942 _selection_changed = true;
3945 case Selection::Extend:
3947 Locations::LocationList ll;
3948 list<ArdourMarker*> to_add;
3950 _editor->selection->markers.range (s, e);
3951 s = min (_marker->position(), s);
3952 e = max (_marker->position(), e);
3955 if (e < max_framepos) {
3958 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3959 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3960 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3963 to_add.push_back (lm->start);
3966 to_add.push_back (lm->end);
3970 if (!to_add.empty()) {
3971 _editor->selection->add (to_add);
3972 _selection_changed = true;
3976 case Selection::Add:
3977 _editor->selection->add (_marker);
3978 _selection_changed = true;
3983 /* Set up copies for us to manipulate during the drag
3986 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3988 Location* l = _editor->find_location_from_marker (*i, is_start);
3995 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3997 /* range: check that the other end of the range isn't
4000 CopiedLocationInfo::iterator x;
4001 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4002 if (*(*x).location == *l) {
4006 if (x == _copied_locations.end()) {
4007 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4009 (*x).markers.push_back (*i);
4010 (*x).move_both = true;
4018 MarkerDrag::setup_pointer_frame_offset ()
4021 Location *location = _editor->find_location_from_marker (_marker, is_start);
4022 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4026 MarkerDrag::motion (GdkEvent* event, bool)
4028 framecnt_t f_delta = 0;
4030 bool move_both = false;
4031 Location *real_location;
4032 Location *copy_location = 0;
4033 framecnt_t const sd = snap_delta (event->button.state);
4035 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4036 framepos_t next = newframe;
4038 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4042 CopiedLocationInfo::iterator x;
4044 /* find the marker we're dragging, and compute the delta */
4046 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4048 copy_location = (*x).location;
4050 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4052 /* this marker is represented by this
4053 * CopiedLocationMarkerInfo
4056 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4061 if (real_location->is_mark()) {
4062 f_delta = newframe - copy_location->start();
4066 switch (_marker->type()) {
4067 case ArdourMarker::SessionStart:
4068 case ArdourMarker::RangeStart:
4069 case ArdourMarker::LoopStart:
4070 case ArdourMarker::PunchIn:
4071 f_delta = newframe - copy_location->start();
4074 case ArdourMarker::SessionEnd:
4075 case ArdourMarker::RangeEnd:
4076 case ArdourMarker::LoopEnd:
4077 case ArdourMarker::PunchOut:
4078 f_delta = newframe - copy_location->end();
4081 /* what kind of marker is this ? */
4090 if (x == _copied_locations.end()) {
4091 /* hmm, impossible - we didn't find the dragged marker */
4095 /* now move them all */
4097 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4099 copy_location = x->location;
4101 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4105 if (real_location->locked()) {
4109 if (copy_location->is_mark()) {
4113 copy_location->set_start (copy_location->start() + f_delta);
4117 framepos_t new_start = copy_location->start() + f_delta;
4118 framepos_t new_end = copy_location->end() + f_delta;
4120 if (is_start) { // start-of-range marker
4122 if (move_both || (*x).move_both) {
4123 copy_location->set_start (new_start);
4124 copy_location->set_end (new_end);
4125 } else if (new_start < copy_location->end()) {
4126 copy_location->set_start (new_start);
4127 } else if (newframe > 0) {
4128 //_editor->snap_to (next, RoundUpAlways, true);
4129 copy_location->set_end (next);
4130 copy_location->set_start (newframe);
4133 } else { // end marker
4135 if (move_both || (*x).move_both) {
4136 copy_location->set_end (new_end);
4137 copy_location->set_start (new_start);
4138 } else if (new_end > copy_location->start()) {
4139 copy_location->set_end (new_end);
4140 } else if (newframe > 0) {
4141 //_editor->snap_to (next, RoundDownAlways, true);
4142 copy_location->set_start (next);
4143 copy_location->set_end (newframe);
4148 update_item (copy_location);
4150 /* now lookup the actual GUI items used to display this
4151 * location and move them to wherever the copy of the location
4152 * is now. This means that the logic in ARDOUR::Location is
4153 * still enforced, even though we are not (yet) modifying
4154 * the real Location itself.
4157 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4160 lm->set_position (copy_location->start(), copy_location->end());
4165 assert (!_copied_locations.empty());
4167 show_verbose_cursor_time (newframe);
4171 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4173 if (!movement_occurred) {
4175 if (was_double_click()) {
4176 _editor->rename_marker (_marker);
4180 /* just a click, do nothing but finish
4181 off the selection process
4184 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4186 case Selection::Set:
4187 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4188 _editor->selection->set (_marker);
4189 _selection_changed = true;
4193 case Selection::Toggle:
4194 /* we toggle on the button release, click only */
4195 _editor->selection->toggle (_marker);
4196 _selection_changed = true;
4200 case Selection::Extend:
4201 case Selection::Add:
4205 if (_selection_changed) {
4206 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4207 _editor->commit_reversible_selection_op();
4213 _editor->_dragging_edit_point = false;
4215 XMLNode &before = _editor->session()->locations()->get_state();
4216 bool in_command = false;
4218 MarkerSelection::iterator i;
4219 CopiedLocationInfo::iterator x;
4222 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4223 x != _copied_locations.end() && i != _editor->selection->markers.end();
4226 Location * location = _editor->find_location_from_marker (*i, is_start);
4230 if (location->locked()) {
4234 _editor->begin_reversible_command ( _("move marker") );
4237 if (location->is_mark()) {
4238 location->set_start (((*x).location)->start());
4240 location->set (((*x).location)->start(), ((*x).location)->end());
4246 XMLNode &after = _editor->session()->locations()->get_state();
4247 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4248 _editor->commit_reversible_command ();
4253 MarkerDrag::aborted (bool movement_occurred)
4255 if (!movement_occurred) {
4259 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4261 /* move all markers to their original location */
4264 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4267 Location * location = _editor->find_location_from_marker (*m, is_start);
4270 (*m)->set_position (is_start ? location->start() : location->end());
4277 MarkerDrag::update_item (Location*)
4282 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4284 , _fixed_grab_x (0.0)
4285 , _fixed_grab_y (0.0)
4286 , _cumulative_x_drag (0.0)
4287 , _cumulative_y_drag (0.0)
4291 if (_zero_gain_fraction < 0.0) {
4292 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4295 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4297 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4303 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4305 Drag::start_grab (event, _editor->cursors()->fader);
4307 // start the grab at the center of the control point so
4308 // the point doesn't 'jump' to the mouse after the first drag
4309 _fixed_grab_x = _point->get_x();
4310 _fixed_grab_y = _point->get_y();
4312 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4313 setup_snap_delta (pos);
4315 float const fraction = 1 - (_point->get_y() / _point->line().height());
4316 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4318 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4320 if (!_point->can_slide ()) {
4321 _x_constrained = true;
4326 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4328 double dx = _drags->current_pointer_x() - last_pointer_x();
4329 double dy = current_pointer_y() - last_pointer_y();
4330 bool need_snap = true;
4332 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4338 /* coordinate in pixels relative to the start of the region (for region-based automation)
4339 or track (for track-based automation) */
4340 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4341 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4343 // calculate zero crossing point. back off by .01 to stay on the
4344 // positive side of zero
4345 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4347 if (_x_constrained) {
4350 if (_y_constrained) {
4354 _cumulative_x_drag = cx - _fixed_grab_x;
4355 _cumulative_y_drag = cy - _fixed_grab_y;
4359 cy = min ((double) _point->line().height(), cy);
4361 // make sure we hit zero when passing through
4362 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4366 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4368 if (!_x_constrained && need_snap) {
4369 _editor->snap_to_with_modifier (cx_frames, event);
4372 cx_frames -= snap_delta (event->button.state);
4373 cx_frames = min (cx_frames, _point->line().maximum_time());
4375 float const fraction = 1.0 - (cy / _point->line().height());
4378 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4379 _editor->begin_reversible_command (_("automation event move"));
4380 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4382 pair<double, float> result;
4383 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4385 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4389 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4391 if (!movement_occurred) {
4394 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4395 _editor->reset_point_selection ();
4399 _point->line().end_drag (_pushing, _final_index);
4400 _editor->commit_reversible_command ();
4405 ControlPointDrag::aborted (bool)
4407 _point->line().reset ();
4411 ControlPointDrag::active (Editing::MouseMode m)
4413 if (m == Editing::MouseDraw) {
4414 /* always active in mouse draw */
4418 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4419 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4422 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4425 , _fixed_grab_x (0.0)
4426 , _fixed_grab_y (0.0)
4427 , _cumulative_y_drag (0)
4431 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4435 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4437 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4440 _item = &_line->grab_item ();
4442 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4443 origin, and ditto for y.
4446 double mx = event->button.x;
4447 double my = event->button.y;
4449 _line->grab_item().canvas_to_item (mx, my);
4451 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4453 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4454 /* no adjacent points */
4458 Drag::start_grab (event, _editor->cursors()->fader);
4460 /* store grab start in item frame */
4461 double const bx = _line->nth (_before)->get_x();
4462 double const ax = _line->nth (_after)->get_x();
4463 double const click_ratio = (ax - mx) / (ax - bx);
4465 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4470 double fraction = 1.0 - (cy / _line->height());
4472 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4476 LineDrag::motion (GdkEvent* event, bool first_move)
4478 double dy = current_pointer_y() - last_pointer_y();
4480 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4484 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4486 _cumulative_y_drag = cy - _fixed_grab_y;
4489 cy = min ((double) _line->height(), cy);
4491 double const fraction = 1.0 - (cy / _line->height());
4495 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4497 _editor->begin_reversible_command (_("automation range move"));
4498 _line->start_drag_line (_before, _after, initial_fraction);
4501 /* we are ignoring x position for this drag, so we can just pass in anything */
4502 pair<double, float> result;
4504 result = _line->drag_motion (0, fraction, true, false, ignored);
4505 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4509 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4511 if (movement_occurred) {
4512 motion (event, false);
4513 _line->end_drag (false, 0);
4514 _editor->commit_reversible_command ();
4516 /* add a new control point on the line */
4518 AutomationTimeAxisView* atv;
4520 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4521 framepos_t where = grab_frame ();
4524 double cy = _fixed_grab_y;
4526 _line->grab_item().item_to_canvas (cx, cy);
4528 atv->add_automation_event (event, where, cy, false);
4529 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4530 AudioRegionView* arv;
4532 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4533 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4540 LineDrag::aborted (bool)
4545 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4549 _region_view_grab_x (0.0),
4550 _cumulative_x_drag (0),
4554 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4558 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4560 Drag::start_grab (event);
4562 _line = reinterpret_cast<Line*> (_item);
4565 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4567 double cx = event->button.x;
4568 double cy = event->button.y;
4570 _item->parent()->canvas_to_item (cx, cy);
4572 /* store grab start in parent frame */
4573 _region_view_grab_x = cx;
4575 _before = *(float*) _item->get_data ("position");
4577 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4579 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4583 FeatureLineDrag::motion (GdkEvent*, bool)
4585 double dx = _drags->current_pointer_x() - last_pointer_x();
4587 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4589 _cumulative_x_drag += dx;
4591 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4600 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4602 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4604 float *pos = new float;
4607 _line->set_data ("position", pos);
4613 FeatureLineDrag::finished (GdkEvent*, bool)
4615 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4616 _arv->update_transient(_before, _before);
4620 FeatureLineDrag::aborted (bool)
4625 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4627 , _vertical_only (false)
4629 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4633 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4635 Drag::start_grab (event);
4636 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4640 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4647 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4649 framepos_t grab = grab_frame ();
4650 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4651 _editor->snap_to_with_modifier (grab, event);
4653 grab = raw_grab_frame ();
4656 /* base start and end on initial click position */
4666 if (current_pointer_y() < grab_y()) {
4667 y1 = current_pointer_y();
4670 y2 = current_pointer_y();
4674 if (start != end || y1 != y2) {
4676 double x1 = _editor->sample_to_pixel (start);
4677 double x2 = _editor->sample_to_pixel (end);
4678 const double min_dimension = 2.0;
4680 if (_vertical_only) {
4681 /* fixed 10 pixel width */
4685 x2 = min (x1 - min_dimension, x2);
4687 x2 = max (x1 + min_dimension, x2);
4692 y2 = min (y1 - min_dimension, y2);
4694 y2 = max (y1 + min_dimension, y2);
4697 /* translate rect into item space and set */
4699 ArdourCanvas::Rect r (x1, y1, x2, y2);
4701 /* this drag is a _trackview_only == true drag, so the y1 and
4702 * y2 (computed using current_pointer_y() and grab_y()) will be
4703 * relative to the top of the trackview group). The
4704 * rubberband rect has the same parent/scroll offset as the
4705 * the trackview group, so we can use the "r" rect directly
4706 * to set the shape of the rubberband.
4709 _editor->rubberband_rect->set (r);
4710 _editor->rubberband_rect->show();
4711 _editor->rubberband_rect->raise_to_top();
4713 show_verbose_cursor_time (pf);
4715 do_select_things (event, true);
4720 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4724 framepos_t grab = grab_frame ();
4725 framepos_t lpf = last_pointer_frame ();
4727 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4728 grab = raw_grab_frame ();
4729 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4743 if (current_pointer_y() < grab_y()) {
4744 y1 = current_pointer_y();
4747 y2 = current_pointer_y();
4751 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4755 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4757 if (movement_occurred) {
4759 motion (event, false);
4760 do_select_things (event, false);
4766 bool do_deselect = true;
4767 MidiTimeAxisView* mtv;
4769 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4771 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4772 /* nothing selected */
4773 add_midi_region (mtv, true);
4774 do_deselect = false;
4778 /* do not deselect if Primary or Tertiary (toggle-select or
4779 * extend-select are pressed.
4782 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4783 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4790 _editor->rubberband_rect->hide();
4794 RubberbandSelectDrag::aborted (bool)
4796 _editor->rubberband_rect->hide ();
4799 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4800 : RegionDrag (e, i, p, v)
4802 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4806 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4808 Drag::start_grab (event, cursor);
4810 _editor->get_selection().add (_primary);
4812 framepos_t where = _primary->region()->position();
4813 setup_snap_delta (where);
4815 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4819 TimeFXDrag::motion (GdkEvent* event, bool)
4821 RegionView* rv = _primary;
4822 StreamView* cv = rv->get_time_axis_view().view ();
4824 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4825 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4826 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4827 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4828 _editor->snap_to_with_modifier (pf, event);
4829 pf -= snap_delta (event->button.state);
4831 if (pf > rv->region()->position()) {
4832 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4835 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4839 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4841 /* this may have been a single click, no drag. We still want the dialog
4842 to show up in that case, so that the user can manually edit the
4843 parameters for the timestretch.
4846 float fraction = 1.0;
4848 if (movement_occurred) {
4850 motion (event, false);
4852 _primary->get_time_axis_view().hide_timestretch ();
4854 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4856 if (adjusted_frame_pos < _primary->region()->position()) {
4857 /* backwards drag of the left edge - not usable */
4861 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4863 fraction = (double) newlen / (double) _primary->region()->length();
4865 #ifndef USE_RUBBERBAND
4866 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4867 if (_primary->region()->data_type() == DataType::AUDIO) {
4868 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4873 if (!_editor->get_selection().regions.empty()) {
4874 /* primary will already be included in the selection, and edit
4875 group shared editing will propagate selection across
4876 equivalent regions, so just use the current region
4880 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4881 error << _("An error occurred while executing time stretch operation") << endmsg;
4887 TimeFXDrag::aborted (bool)
4889 _primary->get_time_axis_view().hide_timestretch ();
4892 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4895 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4899 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4901 Drag::start_grab (event);
4905 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4907 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4911 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4913 if (movement_occurred && _editor->session()) {
4914 /* make sure we stop */
4915 _editor->session()->request_transport_speed (0.0);
4920 ScrubDrag::aborted (bool)
4925 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4929 , _time_selection_at_start (!_editor->get_selection().time.empty())
4931 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4933 if (_time_selection_at_start) {
4934 start_at_start = _editor->get_selection().time.start();
4935 end_at_start = _editor->get_selection().time.end_frame();
4940 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4942 if (_editor->session() == 0) {
4946 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4948 switch (_operation) {
4949 case CreateSelection:
4950 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4955 cursor = _editor->cursors()->selector;
4956 Drag::start_grab (event, cursor);
4959 case SelectionStartTrim:
4960 if (_editor->clicked_axisview) {
4961 _editor->clicked_axisview->order_selection_trims (_item, true);
4963 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4966 case SelectionEndTrim:
4967 if (_editor->clicked_axisview) {
4968 _editor->clicked_axisview->order_selection_trims (_item, false);
4970 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4974 Drag::start_grab (event, cursor);
4977 case SelectionExtend:
4978 Drag::start_grab (event, cursor);
4982 if (_operation == SelectionMove) {
4983 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4985 show_verbose_cursor_time (adjusted_current_frame (event));
4990 SelectionDrag::setup_pointer_frame_offset ()
4992 switch (_operation) {
4993 case CreateSelection:
4994 _pointer_frame_offset = 0;
4997 case SelectionStartTrim:
4999 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5002 case SelectionEndTrim:
5003 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5006 case SelectionExtend:
5012 SelectionDrag::motion (GdkEvent* event, bool first_move)
5014 framepos_t start = 0;
5016 framecnt_t length = 0;
5017 framecnt_t distance = 0;
5019 framepos_t const pending_position = adjusted_current_frame (event);
5021 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5025 switch (_operation) {
5026 case CreateSelection:
5028 framepos_t grab = grab_frame ();
5031 grab = adjusted_current_frame (event, false);
5032 if (grab < pending_position) {
5033 _editor->snap_to (grab, RoundDownMaybe);
5035 _editor->snap_to (grab, RoundUpMaybe);
5039 if (pending_position < grab) {
5040 start = pending_position;
5043 end = pending_position;
5047 /* first drag: Either add to the selection
5048 or create a new selection
5055 /* adding to the selection */
5056 _editor->set_selected_track_as_side_effect (Selection::Add);
5057 _editor->clicked_selection = _editor->selection->add (start, end);
5064 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5065 _editor->set_selected_track_as_side_effect (Selection::Set);
5068 _editor->clicked_selection = _editor->selection->set (start, end);
5072 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5073 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5074 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5076 _editor->selection->add (atest);
5080 /* select all tracks within the rectangle that we've marked out so far */
5081 TrackViewList new_selection;
5082 TrackViewList& all_tracks (_editor->track_views);
5084 ArdourCanvas::Coord const top = grab_y();
5085 ArdourCanvas::Coord const bottom = current_pointer_y();
5087 if (top >= 0 && bottom >= 0) {
5089 //first, find the tracks that are covered in the y range selection
5090 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5091 if ((*i)->covered_by_y_range (top, bottom)) {
5092 new_selection.push_back (*i);
5096 //now find any tracks that are GROUPED with the tracks we selected
5097 TrackViewList grouped_add = new_selection;
5098 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5099 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5100 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5101 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5102 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5103 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5104 grouped_add.push_back (*j);
5109 //now compare our list with the current selection, and add or remove as necessary
5110 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5111 TrackViewList tracks_to_add;
5112 TrackViewList tracks_to_remove;
5113 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5114 if ( !_editor->selection->tracks.contains ( *i ) )
5115 tracks_to_add.push_back ( *i );
5116 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5117 if ( !grouped_add.contains ( *i ) )
5118 tracks_to_remove.push_back ( *i );
5119 _editor->selection->add(tracks_to_add);
5120 _editor->selection->remove(tracks_to_remove);
5126 case SelectionStartTrim:
5128 end = _editor->selection->time[_editor->clicked_selection].end;
5130 if (pending_position > end) {
5133 start = pending_position;
5137 case SelectionEndTrim:
5139 start = _editor->selection->time[_editor->clicked_selection].start;
5141 if (pending_position < start) {
5144 end = pending_position;
5151 start = _editor->selection->time[_editor->clicked_selection].start;
5152 end = _editor->selection->time[_editor->clicked_selection].end;
5154 length = end - start;
5155 distance = pending_position - start;
5156 start = pending_position;
5157 _editor->snap_to (start);
5159 end = start + length;
5163 case SelectionExtend:
5168 switch (_operation) {
5170 if (_time_selection_at_start) {
5171 _editor->selection->move_time (distance);
5175 _editor->selection->replace (_editor->clicked_selection, start, end);
5179 if (_operation == SelectionMove) {
5180 show_verbose_cursor_time(start);
5182 show_verbose_cursor_time(pending_position);
5187 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5189 Session* s = _editor->session();
5191 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5192 if (movement_occurred) {
5193 motion (event, false);
5194 /* XXX this is not object-oriented programming at all. ick */
5195 if (_editor->selection->time.consolidate()) {
5196 _editor->selection->TimeChanged ();
5199 /* XXX what if its a music time selection? */
5201 if (s->get_play_range() && s->transport_rolling()) {
5202 s->request_play_range (&_editor->selection->time, true);
5203 } else if (!s->config.get_external_sync()) {
5204 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5205 if (_operation == SelectionEndTrim)
5206 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5208 s->request_locate (_editor->get_selection().time.start());
5212 if (_editor->get_selection().time.length() != 0) {
5213 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5215 s->clear_range_selection ();
5220 /* just a click, no pointer movement.
5223 if (_operation == SelectionExtend) {
5224 if (_time_selection_at_start) {
5225 framepos_t pos = adjusted_current_frame (event, false);
5226 framepos_t start = min (pos, start_at_start);
5227 framepos_t end = max (pos, end_at_start);
5228 _editor->selection->set (start, end);
5231 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5232 if (_editor->clicked_selection) {
5233 _editor->selection->remove (_editor->clicked_selection);
5236 if (!_editor->clicked_selection) {
5237 _editor->selection->clear_time();
5242 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5243 _editor->selection->set (_editor->clicked_axisview);
5246 if (s && s->get_play_range () && s->transport_rolling()) {
5247 s->request_stop (false, false);
5252 _editor->stop_canvas_autoscroll ();
5253 _editor->clicked_selection = 0;
5254 _editor->commit_reversible_selection_op ();
5258 SelectionDrag::aborted (bool)
5263 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5264 : Drag (e, i, false),
5268 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5270 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5271 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5272 physical_screen_height (_editor->current_toplevel()->get_window())));
5273 _drag_rect->hide ();
5275 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5276 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5279 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5281 /* normal canvas items will be cleaned up when their parent group is deleted. But
5282 this item is created as the child of a long-lived parent group, and so we
5283 need to explicitly delete it.
5289 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5291 if (_editor->session() == 0) {
5295 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5297 if (!_editor->temp_location) {
5298 _editor->temp_location = new Location (*_editor->session());
5301 switch (_operation) {
5302 case CreateSkipMarker:
5303 case CreateRangeMarker:
5304 case CreateTransportMarker:
5305 case CreateCDMarker:
5307 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5312 cursor = _editor->cursors()->selector;
5316 Drag::start_grab (event, cursor);
5318 show_verbose_cursor_time (adjusted_current_frame (event));
5322 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5324 framepos_t start = 0;
5326 ArdourCanvas::Rectangle *crect;
5328 switch (_operation) {
5329 case CreateSkipMarker:
5330 crect = _editor->range_bar_drag_rect;
5332 case CreateRangeMarker:
5333 crect = _editor->range_bar_drag_rect;
5335 case CreateTransportMarker:
5336 crect = _editor->transport_bar_drag_rect;
5338 case CreateCDMarker:
5339 crect = _editor->cd_marker_bar_drag_rect;
5342 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5347 framepos_t const pf = adjusted_current_frame (event);
5349 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5350 framepos_t grab = grab_frame ();
5351 _editor->snap_to (grab);
5353 if (pf < grab_frame()) {
5361 /* first drag: Either add to the selection
5362 or create a new selection.
5367 _editor->temp_location->set (start, end);
5371 update_item (_editor->temp_location);
5373 //_drag_rect->raise_to_top();
5379 _editor->temp_location->set (start, end);
5381 double x1 = _editor->sample_to_pixel (start);
5382 double x2 = _editor->sample_to_pixel (end);
5386 update_item (_editor->temp_location);
5389 show_verbose_cursor_time (pf);
5394 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5396 Location * newloc = 0;
5400 if (movement_occurred) {
5401 motion (event, false);
5404 switch (_operation) {
5405 case CreateSkipMarker:
5406 case CreateRangeMarker:
5407 case CreateCDMarker:
5409 XMLNode &before = _editor->session()->locations()->get_state();
5410 if (_operation == CreateSkipMarker) {
5411 _editor->begin_reversible_command (_("new skip marker"));
5412 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5413 flags = Location::IsRangeMarker | Location::IsSkip;
5414 _editor->range_bar_drag_rect->hide();
5415 } else if (_operation == CreateCDMarker) {
5416 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5417 _editor->begin_reversible_command (_("new CD marker"));
5418 flags = Location::IsRangeMarker | Location::IsCDMarker;
5419 _editor->cd_marker_bar_drag_rect->hide();
5421 _editor->begin_reversible_command (_("new skip marker"));
5422 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5423 flags = Location::IsRangeMarker;
5424 _editor->range_bar_drag_rect->hide();
5426 newloc = new Location (
5427 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5430 _editor->session()->locations()->add (newloc, true);
5431 XMLNode &after = _editor->session()->locations()->get_state();
5432 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5433 _editor->commit_reversible_command ();
5437 case CreateTransportMarker:
5438 // popup menu to pick loop or punch
5439 _editor->new_transport_marker_context_menu (&event->button, _item);
5445 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5447 if (_operation == CreateTransportMarker) {
5449 /* didn't drag, so just locate */
5451 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5453 } else if (_operation == CreateCDMarker) {
5455 /* didn't drag, but mark is already created so do
5458 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5463 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5465 if (end == max_framepos) {
5466 end = _editor->session()->current_end_frame ();
5469 if (start == max_framepos) {
5470 start = _editor->session()->current_start_frame ();
5473 switch (_editor->mouse_mode) {
5475 /* find the two markers on either side and then make the selection from it */
5476 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5480 /* find the two markers on either side of the click and make the range out of it */
5481 _editor->selection->set (start, end);
5490 _editor->stop_canvas_autoscroll ();
5494 RangeMarkerBarDrag::aborted (bool movement_occurred)
5496 if (movement_occurred) {
5497 _drag_rect->hide ();
5502 RangeMarkerBarDrag::update_item (Location* location)
5504 double const x1 = _editor->sample_to_pixel (location->start());
5505 double const x2 = _editor->sample_to_pixel (location->end());
5507 _drag_rect->set_x0 (x1);
5508 _drag_rect->set_x1 (x2);
5511 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5513 , _cumulative_dx (0)
5514 , _cumulative_dy (0)
5515 , _was_selected (false)
5517 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5519 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5521 _region = &_primary->region_view ();
5522 _note_height = _region->midi_stream_view()->note_height ();
5526 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5528 Drag::start_grab (event);
5529 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5531 if (!(_was_selected = _primary->selected())) {
5533 /* tertiary-click means extend selection - we'll do that on button release,
5534 so don't add it here, because otherwise we make it hard to figure
5535 out the "extend-to" range.
5538 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5541 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5544 _region->note_selected (_primary, true);
5546 _editor->get_selection().clear_points();
5547 _region->unique_select (_primary);
5553 /** @return Current total drag x change in frames */
5555 NoteDrag::total_dx (const guint state) const
5558 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5560 /* primary note time */
5561 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5563 /* new time of the primary note in session frames */
5564 frameoffset_t st = n + dx + snap_delta (state);
5566 framepos_t const rp = _region->region()->position ();
5568 /* prevent the note being dragged earlier than the region's position */
5571 /* possibly snap and return corresponding delta */
5575 if (ArdourKeyboard::indicates_snap (state)) {
5576 if (_editor->snap_mode () != SnapOff) {
5580 if (_editor->snap_mode () == SnapOff) {
5582 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5583 if (ArdourKeyboard::indicates_snap_delta (state)) {
5591 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5592 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5594 ret = st - n - snap_delta (state);
5599 /** @return Current total drag y change in note number */
5601 NoteDrag::total_dy () const
5603 MidiStreamView* msv = _region->midi_stream_view ();
5604 double const y = _region->midi_view()->y_position ();
5605 /* new current note */
5606 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5608 n = max (msv->lowest_note(), n);
5609 n = min (msv->highest_note(), n);
5610 /* and work out delta */
5611 return n - msv->y_to_note (grab_y() - y);
5615 NoteDrag::motion (GdkEvent * event, bool)
5617 /* Total change in x and y since the start of the drag */
5618 frameoffset_t const dx = total_dx (event->button.state);
5619 int8_t const dy = total_dy ();
5621 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5622 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5623 double const tdy = -dy * _note_height - _cumulative_dy;
5626 _cumulative_dx += tdx;
5627 _cumulative_dy += tdy;
5629 int8_t note_delta = total_dy();
5631 _region->move_selection (tdx, tdy, note_delta);
5633 /* the new note value may be the same as the old one, but we
5634 * don't know what that means because the selection may have
5635 * involved more than one note and we might be doing something
5636 * odd with them. so show the note value anyway, always.
5639 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5641 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5646 NoteDrag::finished (GdkEvent* ev, bool moved)
5649 /* no motion - select note */
5651 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5652 _editor->current_mouse_mode() == Editing::MouseDraw) {
5654 bool changed = false;
5656 if (_was_selected) {
5657 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5659 _region->note_deselected (_primary);
5662 _editor->get_selection().clear_points();
5663 _region->unique_select (_primary);
5667 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5668 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5670 if (!extend && !add && _region->selection_size() > 1) {
5671 _editor->get_selection().clear_points();
5672 _region->unique_select (_primary);
5674 } else if (extend) {
5675 _region->note_selected (_primary, true, true);
5678 /* it was added during button press */
5685 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5686 _editor->commit_reversible_selection_op();
5690 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5695 NoteDrag::aborted (bool)
5700 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5701 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5702 : Drag (editor, atv->base_item ())
5704 , _y_origin (atv->y_position())
5705 , _nothing_to_drag (false)
5707 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5708 setup (atv->lines ());
5711 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5712 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5713 : Drag (editor, rv->get_canvas_group ())
5715 , _y_origin (rv->get_time_axis_view().y_position())
5716 , _nothing_to_drag (false)
5719 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5721 list<boost::shared_ptr<AutomationLine> > lines;
5723 AudioRegionView* audio_view;
5724 AutomationRegionView* automation_view;
5725 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5726 lines.push_back (audio_view->get_gain_line ());
5727 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5728 lines.push_back (automation_view->line ());
5731 error << _("Automation range drag created for invalid region type") << endmsg;
5737 /** @param lines AutomationLines to drag.
5738 * @param offset Offset from the session start to the points in the AutomationLines.
5741 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5743 /* find the lines that overlap the ranges being dragged */
5744 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5745 while (i != lines.end ()) {
5746 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5749 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5751 /* check this range against all the AudioRanges that we are using */
5752 list<AudioRange>::const_iterator k = _ranges.begin ();
5753 while (k != _ranges.end()) {
5754 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5760 /* add it to our list if it overlaps at all */
5761 if (k != _ranges.end()) {
5766 _lines.push_back (n);
5772 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5776 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5778 return 1.0 - ((global_y - _y_origin) / line->height());
5782 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5784 const double v = list->eval(x);
5785 return _integral ? rint(v) : v;
5789 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5791 Drag::start_grab (event, cursor);
5793 /* Get line states before we start changing things */
5794 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5795 i->state = &i->line->get_state ();
5796 i->original_fraction = y_fraction (i->line, current_pointer_y());
5799 if (_ranges.empty()) {
5801 /* No selected time ranges: drag all points */
5802 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5803 uint32_t const N = i->line->npoints ();
5804 for (uint32_t j = 0; j < N; ++j) {
5805 i->points.push_back (i->line->nth (j));
5811 if (_nothing_to_drag) {
5817 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5819 if (_nothing_to_drag && !first_move) {
5824 _editor->begin_reversible_command (_("automation range move"));
5826 if (!_ranges.empty()) {
5828 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5830 framecnt_t const half = (i->start + i->end) / 2;
5832 /* find the line that this audio range starts in */
5833 list<Line>::iterator j = _lines.begin();
5834 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5838 if (j != _lines.end()) {
5839 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5841 /* j is the line that this audio range starts in; fade into it;
5842 64 samples length plucked out of thin air.
5845 framepos_t a = i->start + 64;
5850 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5851 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5853 XMLNode &before = the_list->get_state();
5854 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5855 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5857 if (add_p || add_q) {
5858 _editor->session()->add_command (
5859 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5863 /* same thing for the end */
5866 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5870 if (j != _lines.end()) {
5871 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5873 /* j is the line that this audio range starts in; fade out of it;
5874 64 samples length plucked out of thin air.
5877 framepos_t b = i->end - 64;
5882 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5883 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5885 XMLNode &before = the_list->get_state();
5886 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5887 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5889 if (add_p || add_q) {
5890 _editor->session()->add_command (
5891 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5896 _nothing_to_drag = true;
5898 /* Find all the points that should be dragged and put them in the relevant
5899 points lists in the Line structs.
5902 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5904 uint32_t const N = i->line->npoints ();
5905 for (uint32_t j = 0; j < N; ++j) {
5907 /* here's a control point on this line */
5908 ControlPoint* p = i->line->nth (j);
5909 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5911 /* see if it's inside a range */
5912 list<AudioRange>::const_iterator k = _ranges.begin ();
5913 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5917 if (k != _ranges.end()) {
5918 /* dragging this point */
5919 _nothing_to_drag = false;
5920 i->points.push_back (p);
5926 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5927 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5931 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5932 float const f = y_fraction (l->line, current_pointer_y());
5933 /* we are ignoring x position for this drag, so we can just pass in anything */
5934 pair<double, float> result;
5936 result = l->line->drag_motion (0, f, true, false, ignored);
5937 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5942 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5944 if (_nothing_to_drag || !motion_occurred) {
5948 motion (event, false);
5949 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5950 i->line->end_drag (false, 0);
5953 _editor->commit_reversible_command ();
5957 AutomationRangeDrag::aborted (bool)
5959 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5964 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5966 , initial_time_axis_view (itav)
5968 /* note that time_axis_view may be null if the regionview was created
5969 * as part of a copy operation.
5971 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5972 layer = v->region()->layer ();
5973 initial_y = v->get_canvas_group()->position().y;
5974 initial_playlist = v->region()->playlist ();
5975 initial_position = v->region()->position ();
5976 initial_end = v->region()->position () + v->region()->length ();
5979 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5980 : Drag (e, i->canvas_item ())
5983 , _cumulative_dx (0)
5985 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5986 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5991 PatchChangeDrag::motion (GdkEvent* ev, bool)
5993 framepos_t f = adjusted_current_frame (ev);
5994 boost::shared_ptr<Region> r = _region_view->region ();
5995 f = max (f, r->position ());
5996 f = min (f, r->last_frame ());
5998 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5999 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6000 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6001 _cumulative_dx = dxu;
6005 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6007 if (!movement_occurred) {
6008 if (was_double_click()) {
6009 _region_view->edit_patch_change (_patch_change);
6014 boost::shared_ptr<Region> r (_region_view->region ());
6015 framepos_t f = adjusted_current_frame (ev);
6016 f = max (f, r->position ());
6017 f = min (f, r->last_frame ());
6019 _region_view->move_patch_change (
6021 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6026 PatchChangeDrag::aborted (bool)
6028 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6032 PatchChangeDrag::setup_pointer_frame_offset ()
6034 boost::shared_ptr<Region> region = _region_view->region ();
6035 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6038 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6039 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6046 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6048 _region_view->update_drag_selection (
6050 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6054 MidiRubberbandSelectDrag::deselect_things ()
6059 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6060 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6063 _vertical_only = true;
6067 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6069 double const y = _region_view->midi_view()->y_position ();
6071 y1 = max (0.0, y1 - y);
6072 y2 = max (0.0, y2 - y);
6074 _region_view->update_vertical_drag_selection (
6077 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6082 MidiVerticalSelectDrag::deselect_things ()
6087 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6088 : RubberbandSelectDrag (e, i)
6094 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6096 if (drag_in_progress) {
6097 /* We just want to select things at the end of the drag, not during it */
6101 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6103 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6105 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6107 _editor->commit_reversible_selection_op ();
6111 EditorRubberbandSelectDrag::deselect_things ()
6113 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6115 _editor->selection->clear_tracks();
6116 _editor->selection->clear_regions();
6117 _editor->selection->clear_points ();
6118 _editor->selection->clear_lines ();
6119 _editor->selection->clear_midi_notes ();
6121 _editor->commit_reversible_selection_op();
6124 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6129 _note[0] = _note[1] = 0;
6132 NoteCreateDrag::~NoteCreateDrag ()
6138 NoteCreateDrag::grid_frames (framepos_t t) const
6141 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6143 grid_beats = Evoral::Beats(1);
6146 return _region_view->region_beats_to_region_frames (grid_beats);
6150 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6152 Drag::start_grab (event, cursor);
6154 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6156 framepos_t pf = _drags->current_pointer_frame ();
6157 framecnt_t const g = grid_frames (pf);
6159 /* Hack so that we always snap to the note that we are over, instead of snapping
6160 to the next one if we're more than halfway through the one we're over.
6162 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6166 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6167 _note[1] = _note[0];
6169 MidiStreamView* sv = _region_view->midi_stream_view ();
6170 double const x = _editor->sample_to_pixel (_note[0]);
6171 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6173 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6174 _drag_rect->set_outline_all ();
6175 _drag_rect->set_outline_color (0xffffff99);
6176 _drag_rect->set_fill_color (0xffffff66);
6180 NoteCreateDrag::motion (GdkEvent* event, bool)
6182 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6183 double const x0 = _editor->sample_to_pixel (_note[0]);
6184 double const x1 = _editor->sample_to_pixel (_note[1]);
6185 _drag_rect->set_x0 (std::min(x0, x1));
6186 _drag_rect->set_x1 (std::max(x0, x1));
6190 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6192 if (!had_movement) {
6196 framepos_t const start = min (_note[0], _note[1]);
6197 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6199 framecnt_t const g = grid_frames (start);
6200 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6202 if (_editor->snap_mode() == SnapNormal && length < g) {
6206 Evoral::Beats length_beats = max (
6207 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6209 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6213 NoteCreateDrag::y_to_region (double y) const
6216 _region_view->get_canvas_group()->canvas_to_item (x, y);
6221 NoteCreateDrag::aborted (bool)
6226 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6231 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6235 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6237 Drag::start_grab (event, cursor);
6241 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6247 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6250 distance = _drags->current_pointer_x() - grab_x();
6251 len = ar->fade_in()->back()->when;
6253 distance = grab_x() - _drags->current_pointer_x();
6254 len = ar->fade_out()->back()->when;
6257 /* how long should it be ? */
6259 new_length = len + _editor->pixel_to_sample (distance);
6261 /* now check with the region that this is legal */
6263 new_length = ar->verify_xfade_bounds (new_length, start);
6266 arv->reset_fade_in_shape_width (ar, new_length);
6268 arv->reset_fade_out_shape_width (ar, new_length);
6273 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6279 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6282 distance = _drags->current_pointer_x() - grab_x();
6283 len = ar->fade_in()->back()->when;
6285 distance = grab_x() - _drags->current_pointer_x();
6286 len = ar->fade_out()->back()->when;
6289 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6291 _editor->begin_reversible_command ("xfade trim");
6292 ar->playlist()->clear_owned_changes ();
6295 ar->set_fade_in_length (new_length);
6297 ar->set_fade_out_length (new_length);
6300 /* Adjusting the xfade may affect other regions in the playlist, so we need
6301 to get undo Commands from the whole playlist rather than just the
6305 vector<Command*> cmds;
6306 ar->playlist()->rdiff (cmds);
6307 _editor->session()->add_commands (cmds);
6308 _editor->commit_reversible_command ();
6313 CrossfadeEdgeDrag::aborted (bool)
6316 // arv->redraw_start_xfade ();
6318 // arv->redraw_end_xfade ();
6322 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6323 : Drag (e, item, true)
6324 , line (new EditorCursor (*e))
6326 line->set_position (pos);
6330 RegionCutDrag::~RegionCutDrag ()
6336 RegionCutDrag::motion (GdkEvent*, bool)
6338 framepos_t where = _drags->current_pointer_frame();
6339 _editor->snap_to (where);
6341 line->set_position (where);
6345 RegionCutDrag::finished (GdkEvent*, bool)
6347 _editor->get_track_canvas()->canvas()->re_enter();
6349 framepos_t pos = _drags->current_pointer_frame();
6353 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6359 _editor->split_regions_at (pos, rs);
6363 RegionCutDrag::aborted (bool)