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)
240 , _constraint_pressed (false)
246 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
252 _cursor_ctx = CursorContext::create (*_editor, cursor);
254 _cursor_ctx->change (cursor);
261 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
264 /* we set up x/y dragging constraints on first move */
265 _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
267 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
269 setup_pointer_frame_offset ();
270 _grab_frame = adjusted_frame (_raw_grab_frame, event);
271 _last_pointer_frame = _grab_frame;
272 _last_pointer_x = _grab_x;
274 if (_trackview_only) {
275 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
278 _last_pointer_y = _grab_y;
282 if (!_editor->cursors()->is_invalid (cursor)) {
283 /* CAIROCANVAS need a variant here that passes *cursor */
284 _cursor_ctx = CursorContext::create (*_editor, cursor);
287 if (_editor->session() && _editor->session()->transport_rolling()) {
290 _was_rolling = false;
293 switch (_editor->snap_type()) {
294 case SnapToRegionStart:
295 case SnapToRegionEnd:
296 case SnapToRegionSync:
297 case SnapToRegionBoundary:
298 _editor->build_region_boundary_cache ();
305 /** Call to end a drag `successfully'. Ungrabs item and calls
306 * subclass' finished() method.
308 * @param event GDK event, or 0.
309 * @return true if some movement occurred, otherwise false.
312 Drag::end_grab (GdkEvent* event)
314 _editor->stop_canvas_autoscroll ();
318 finished (event, _move_threshold_passed);
320 _editor->verbose_cursor()->hide ();
323 return _move_threshold_passed;
327 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
331 if (f > _pointer_frame_offset) {
332 pos = f - _pointer_frame_offset;
336 _editor->snap_to_with_modifier (pos, event);
343 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
345 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
349 Drag::snap_delta (guint state) const
351 if (ArdourKeyboard::indicates_snap_delta (state)) {
359 Drag::current_pointer_x() const
361 return _drags->current_pointer_x ();
365 Drag::current_pointer_y () const
367 if (!_trackview_only) {
368 return _drags->current_pointer_y ();
371 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
375 Drag::setup_snap_delta (framepos_t pos)
377 framepos_t temp = pos;
378 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
379 _snap_delta = temp - pos;
383 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
385 /* check to see if we have moved in any way that matters since the last motion event */
386 if (_move_threshold_passed &&
387 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
388 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
392 pair<framecnt_t, int> const threshold = move_threshold ();
394 bool const old_move_threshold_passed = _move_threshold_passed;
396 if (!_move_threshold_passed) {
398 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
399 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
401 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
404 if (active (_editor->mouse_mode) && _move_threshold_passed) {
406 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
408 if (old_move_threshold_passed != _move_threshold_passed) {
412 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
413 _initially_vertical = true;
415 _initially_vertical = false;
417 /** check constraints for this drag.
418 * Note that the current convention is to use "contains" for
419 * key modifiers during motion and "equals" when initiating a drag.
420 * In this case we haven't moved yet, so "equals" applies here.
422 if (Config->get_edit_mode() != Lock) {
423 if (event->motion.state & Gdk::BUTTON2_MASK) {
424 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
425 if (_constraint_pressed) {
426 _x_constrained = false;
427 _y_constrained = true;
429 _x_constrained = true;
430 _y_constrained = false;
432 } else if (_constraint_pressed) {
433 // if dragging normally, the motion is constrained to the first direction of movement.
434 if (_initially_vertical) {
435 _x_constrained = true;
436 _y_constrained = false;
438 _x_constrained = false;
439 _y_constrained = true;
443 if (event->button.state & Gdk::BUTTON2_MASK) {
444 _x_constrained = false;
446 _x_constrained = true;
448 _y_constrained = false;
452 if (!from_autoscroll) {
453 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
456 if (!_editor->autoscroll_active() || from_autoscroll) {
459 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
461 motion (event, first_move && !_starting_point_passed);
463 if (first_move && !_starting_point_passed) {
464 _starting_point_passed = true;
467 _last_pointer_x = _drags->current_pointer_x ();
468 _last_pointer_y = current_pointer_y ();
469 _last_pointer_frame = adjusted_current_frame (event, false);
479 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
487 aborted (_move_threshold_passed);
489 _editor->stop_canvas_autoscroll ();
490 _editor->verbose_cursor()->hide ();
494 Drag::show_verbose_cursor_time (framepos_t frame)
496 _editor->verbose_cursor()->set_time (frame);
497 _editor->verbose_cursor()->show ();
501 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
503 _editor->verbose_cursor()->set_duration (start, end);
504 _editor->verbose_cursor()->show ();
508 Drag::show_verbose_cursor_text (string const & text)
510 _editor->verbose_cursor()->set (text);
511 _editor->verbose_cursor()->show ();
514 boost::shared_ptr<Region>
515 Drag::add_midi_region (MidiTimeAxisView* view, bool commit, const int32_t sub_num)
517 if (_editor->session()) {
518 const TempoMap& map (_editor->session()->tempo_map());
519 framecnt_t pos = grab_frame();
520 /* not that the frame rate used here can be affected by pull up/down which
523 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
524 return view->add_region (grab_frame(), len, commit, sub_num);
527 return boost::shared_ptr<Region>();
530 struct PresentationInfoTimeAxisViewSorter {
531 bool operator() (TimeAxisView* a, TimeAxisView* b) {
532 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
536 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
541 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
543 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
544 as some of the regions we are dragging may be on such tracks.
547 TrackViewList track_views = _editor->track_views;
548 track_views.sort (PresentationInfoTimeAxisViewSorter ());
550 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
551 _time_axis_views.push_back (*i);
553 TimeAxisView::Children children_list = (*i)->get_child_list ();
554 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
555 _time_axis_views.push_back (j->get());
559 /* the list of views can be empty at this point if this is a region list-insert drag
562 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
563 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
566 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
570 RegionDrag::region_going_away (RegionView* v)
572 list<DraggingView>::iterator i = _views.begin ();
573 while (i != _views.end() && i->view != v) {
577 if (i != _views.end()) {
582 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
583 * or -1 if it is not found.
586 RegionDrag::find_time_axis_view (TimeAxisView* t) const
589 int const N = _time_axis_views.size ();
590 while (i < N && _time_axis_views[i] != t) {
594 if (_time_axis_views[i] != t) {
601 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
602 : RegionDrag (e, i, p, v)
604 , _ignore_video_lock (false)
606 , _last_pointer_time_axis_view (0)
607 , _last_pointer_layer (0)
612 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
616 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
618 Drag::start_grab (event, cursor);
619 setup_snap_delta (_last_frame_position);
621 show_verbose_cursor_time (_last_frame_position);
623 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
625 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
626 assert(_last_pointer_time_axis_view >= 0);
627 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
630 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
631 _ignore_video_lock = true;
635 /* cross track dragging seems broken here. disabled for now. */
636 _y_constrained = true;
641 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
643 /* compute the amount of pointer motion in frames, and where
644 the region would be if we moved it by that much.
646 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
648 framepos_t sync_frame;
649 framecnt_t sync_offset;
652 sync_offset = _primary->region()->sync_offset (sync_dir);
654 /* we don't handle a sync point that lies before zero.
656 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
658 framecnt_t const sd = snap_delta (event->button.state);
659 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
661 _editor->snap_to_with_modifier (sync_frame, event);
663 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
666 *pending_region_position = _last_frame_position;
669 if (*pending_region_position > max_framepos - _primary->region()->length()) {
670 *pending_region_position = _last_frame_position;
675 bool const x_move_allowed = !_x_constrained;
677 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
679 /* x movement since last time (in pixels) */
680 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
682 /* total x movement */
683 framecnt_t total_dx = *pending_region_position;
684 if (regions_came_from_canvas()) {
685 total_dx = total_dx - grab_frame ();
688 /* check that no regions have gone off the start of the session */
689 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
690 if ((i->view->region()->position() + total_dx) < 0) {
692 *pending_region_position = _last_frame_position;
703 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
709 const int tavsize = _time_axis_views.size();
710 const int dt = delta > 0 ? +1 : -1;
712 int target = start + delta - skip;
714 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
715 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
717 while (current >= 0 && current != target) {
719 if (current < 0 && dt < 0) {
722 if (current >= tavsize && dt > 0) {
725 if (current < 0 || current >= tavsize) {
729 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
730 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
734 if (distance_only && current == start + delta) {
742 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
744 if (_y_constrained) {
748 const int tavsize = _time_axis_views.size();
749 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
750 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
751 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
753 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
754 /* already in the drop zone */
755 if (delta_track >= 0) {
756 /* downward motion - OK if others are still not in the dropzone */
765 } else if (n >= tavsize) {
766 /* downward motion into drop zone. That's fine. */
770 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
771 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
772 /* not a track, or the wrong type */
776 double const l = i->layer + delta_layer;
778 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
779 mode to allow the user to place a region below another on layer 0.
781 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
782 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
783 If it has, the layers will be munged later anyway, so it's ok.
789 /* all regions being dragged are ok with this change */
793 struct DraggingViewSorter {
794 bool operator() (const DraggingView& a, const DraggingView& b) {
795 return a.time_axis_view < b.time_axis_view;
800 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
802 double delta_layer = 0;
803 int delta_time_axis_view = 0;
804 int current_pointer_time_axis_view = -1;
806 assert (!_views.empty ());
808 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
810 /* Find the TimeAxisView that the pointer is now over */
811 const double cur_y = current_pointer_y ();
812 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
813 TimeAxisView* tv = r.first;
815 if (!tv && cur_y < 0) {
816 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
820 /* find drop-zone y-position */
821 Coord last_track_bottom_edge;
822 last_track_bottom_edge = 0;
823 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
824 if (!(*t)->hidden()) {
825 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
830 if (tv && tv->view()) {
831 /* the mouse is over a track */
832 double layer = r.second;
834 if (first_move && tv->view()->layer_display() == Stacked) {
835 tv->view()->set_layer_display (Expanded);
838 /* Here's the current pointer position in terms of time axis view and layer */
839 current_pointer_time_axis_view = find_time_axis_view (tv);
840 assert(current_pointer_time_axis_view >= 0);
842 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
844 /* Work out the change in y */
846 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
847 if (!rtv || !rtv->is_track()) {
848 /* ignore non-tracks early on. we can't move any regions on them */
849 } else if (_last_pointer_time_axis_view < 0) {
850 /* Was in the drop-zone, now over a track.
851 * Hence it must be an upward move (from the bottom)
853 * track_index is still -1, so delta must be set to
854 * move up the correct number of tracks from the bottom.
856 * This is necessary because steps may be skipped if
857 * the bottom-most track is not a valid target and/or
858 * if there are hidden tracks at the bottom.
859 * Hence the initial offset (_ddropzone) as well as the
860 * last valid pointer position (_pdropzone) need to be
861 * taken into account.
863 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
865 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
868 /* TODO needs adjustment per DraggingView,
870 * e.g. select one region on the top-layer of a track
871 * and one region which is at the bottom-layer of another track
874 * Indicated drop-zones and layering is wrong.
875 * and may infer additional layers on the target-track
876 * (depending how many layers the original track had).
878 * Or select two regions (different layers) on a same track,
879 * move across a non-layer track.. -> layering info is lost.
880 * on drop either of the regions may be on top.
882 * Proposed solution: screw it :) well,
883 * don't use delta_layer, use an absolute value
884 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
885 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
886 * 3) iterate over all DraggingView, find the one that is over the track with most layers
887 * 4) proportionally scale layer to layers available on target
889 delta_layer = current_pointer_layer - _last_pointer_layer;
892 /* for automation lanes, there is a TimeAxisView but no ->view()
893 * if (!tv) -> dropzone
895 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
896 /* Moving into the drop-zone.. */
897 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
898 /* delta_time_axis_view may not be sufficient to move into the DZ
899 * the mouse may enter it, but it may not be a valid move due to
902 * -> remember the delta needed to move into the dropzone
904 _ddropzone = delta_time_axis_view;
905 /* ..but subtract hidden tracks (or routes) at the bottom.
906 * we silently move mover them
908 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
909 - _time_axis_views.size();
911 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
912 /* move around inside the zone.
913 * This allows to move further down until all regions are in the zone.
915 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
916 assert(ptr_y >= last_track_bottom_edge);
917 assert(_ddropzone > 0);
919 /* calculate mouse position in 'tracks' below last track. */
920 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
921 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
923 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
925 delta_time_axis_view = dzpos - _pdropzone;
926 } else if (dzpos < _pdropzone && _ndropzone > 0) {
927 // move up inside the DZ
928 delta_time_axis_view = dzpos - _pdropzone;
932 /* Work out the change in x */
933 framepos_t pending_region_position;
934 double const x_delta = compute_x_delta (event, &pending_region_position);
935 _last_frame_position = pending_region_position;
937 /* calculate hidden tracks in current y-axis delta */
939 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
940 /* The mouse is more than one track below the dropzone.
941 * distance calculation is not needed (and would not work, either
942 * because the dropzone is "packed").
944 * Except when [partially] moving regions out of dropzone in a large step.
945 * (the mouse may or may not remain in the DZ)
946 * Hidden tracks at the bottom of the TAV need to be skipped.
948 * This also handles the case if the mouse entered the DZ
949 * in a large step (exessive delta), either due to fast-movement,
950 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
952 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
953 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
955 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
956 -_time_axis_views.size() - dt;
959 else if (_last_pointer_time_axis_view < 0) {
960 /* Moving out of the zone. Check for hidden tracks at the bottom. */
961 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
962 -_time_axis_views.size() - delta_time_axis_view;
964 /* calculate hidden tracks that are skipped by the pointer movement */
965 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
966 - _last_pointer_time_axis_view
967 - delta_time_axis_view;
970 /* Verify change in y */
971 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
972 /* this y movement is not allowed, so do no y movement this time */
973 delta_time_axis_view = 0;
978 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
979 /* haven't reached next snap point, and we're not switching
980 trackviews nor layers. nothing to do.
985 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
986 PlaylistDropzoneMap playlist_dropzone_map;
987 _ndropzone = 0; // number of elements currently in the dropzone
990 /* sort views by time_axis.
991 * This retains track order in the dropzone, regardless
992 * of actual selection order
994 _views.sort (DraggingViewSorter());
996 /* count number of distinct tracks of all regions
997 * being dragged, used for dropzone.
1000 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1001 if (i->time_axis_view != prev_track) {
1002 prev_track = i->time_axis_view;
1008 _views.back().time_axis_view -
1009 _views.front().time_axis_view;
1011 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1012 - _views.back().time_axis_view;
1014 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1018 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1020 RegionView* rv = i->view;
1025 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1032 /* reparent the regionview into a group above all
1036 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1037 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1038 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1039 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1040 /* move the item so that it continues to appear at the
1041 same location now that its parent has changed.
1043 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1046 /* If we have moved tracks, we'll fudge the layer delta so that the
1047 region gets moved back onto layer 0 on its new track; this avoids
1048 confusion when dragging regions from non-zero layers onto different
1051 double this_delta_layer = delta_layer;
1052 if (delta_time_axis_view != 0) {
1053 this_delta_layer = - i->layer;
1056 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1058 int track_index = i->time_axis_view + this_delta_time_axis_view;
1059 assert(track_index >= 0);
1061 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1062 /* Track is in the Dropzone */
1064 i->time_axis_view = track_index;
1065 assert(i->time_axis_view >= (int) _time_axis_views.size());
1068 double yposition = 0;
1069 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1070 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1073 /* store index of each new playlist as a negative count, starting at -1 */
1075 if (pdz == playlist_dropzone_map.end()) {
1076 /* compute where this new track (which doesn't exist yet) will live
1079 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1081 /* How high is this region view ? */
1083 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1084 ArdourCanvas::Rect bbox;
1087 bbox = obbox.get ();
1090 last_track_bottom_edge += bbox.height();
1092 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1095 yposition = pdz->second;
1098 /* values are zero or negative, hence the use of min() */
1099 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1104 /* The TimeAxisView that this region is now over */
1105 TimeAxisView* current_tv = _time_axis_views[track_index];
1107 /* Ensure it is moved from stacked -> expanded if appropriate */
1108 if (current_tv->view()->layer_display() == Stacked) {
1109 current_tv->view()->set_layer_display (Expanded);
1112 /* We're only allowed to go -ve in layer on Expanded views */
1113 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1114 this_delta_layer = - i->layer;
1118 rv->set_height (current_tv->view()->child_height ());
1120 /* Update show/hidden status as the region view may have come from a hidden track,
1121 or have moved to one.
1123 if (current_tv->hidden ()) {
1124 rv->get_canvas_group()->hide ();
1126 rv->get_canvas_group()->show ();
1129 /* Update the DraggingView */
1130 i->time_axis_view = track_index;
1131 i->layer += this_delta_layer;
1134 _editor->mouse_brush_insert_region (rv, pending_region_position);
1138 /* Get the y coordinate of the top of the track that this region is now over */
1139 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1141 /* And adjust for the layer that it should be on */
1142 StreamView* cv = current_tv->view ();
1143 switch (cv->layer_display ()) {
1147 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1150 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1154 /* need to get the parent of the regionview
1155 * canvas group and get its position in
1156 * equivalent coordinate space as the trackview
1157 * we are now dragging over.
1160 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1165 /* Now move the region view */
1166 rv->move (x_delta, y_delta);
1168 } /* foreach region */
1170 _total_x_delta += x_delta;
1172 if (x_delta != 0 && !_brushing) {
1173 show_verbose_cursor_time (_last_frame_position);
1176 /* keep track of pointer movement */
1178 /* the pointer is currently over a time axis view */
1180 if (_last_pointer_time_axis_view < 0) {
1181 /* last motion event was not over a time axis view
1182 * or last y-movement out of the dropzone was not valid
1185 if (delta_time_axis_view < 0) {
1186 /* in the drop zone, moving up */
1188 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1189 * We do not use negative _last_pointer_time_axis_view because
1190 * the dropzone is "packed" (the actual track offset is ignored)
1192 * As opposed to the actual number
1193 * of elements in the dropzone (_ndropzone)
1194 * _pdropzone is not constrained. This is necessary
1195 * to allow moving multiple regions with y-distance
1198 * There can be 0 elements in the dropzone,
1199 * even though the drag-pointer is inside the DZ.
1202 * [ Audio-track, Midi-track, Audio-track, DZ ]
1203 * move regions from both audio tracks at the same time into the
1204 * DZ by grabbing the region in the bottom track.
1206 assert(current_pointer_time_axis_view >= 0);
1207 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1211 /* only move out of the zone if the movement is OK */
1212 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1213 assert(delta_time_axis_view < 0);
1214 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1215 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1216 * the current position can be calculated as follows:
1218 // a well placed oofus attack can still throw this off.
1219 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1220 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1223 /* last motion event was also over a time axis view */
1224 _last_pointer_time_axis_view += delta_time_axis_view;
1225 assert(_last_pointer_time_axis_view >= 0);
1230 /* the pointer is not over a time axis view */
1231 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1232 _pdropzone += delta_time_axis_view - delta_skip;
1233 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1236 _last_pointer_layer += delta_layer;
1240 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1242 if (_copy && first_move) {
1243 if (_x_constrained && !_brushing) {
1244 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1245 } else if (!_brushing) {
1246 _editor->begin_reversible_command (Operations::region_copy);
1247 } else if (_brushing) {
1248 _editor->begin_reversible_command (Operations::drag_region_brush);
1250 /* duplicate the regionview(s) and region(s) */
1252 list<DraggingView> new_regionviews;
1254 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1256 RegionView* rv = i->view;
1257 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1258 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1260 const boost::shared_ptr<const Region> original = rv->region();
1261 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true
1262 , _editor->get_grid_music_divisions (event->button.state));
1263 /* need to set this so that the drop zone code can work. This doesn't
1264 actually put the region into the playlist, but just sets a weak pointer
1267 region_copy->set_playlist (original->playlist());
1271 boost::shared_ptr<AudioRegion> audioregion_copy
1272 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1274 nrv = new AudioRegionView (*arv, audioregion_copy);
1276 boost::shared_ptr<MidiRegion> midiregion_copy
1277 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1278 nrv = new MidiRegionView (*mrv, midiregion_copy);
1283 nrv->get_canvas_group()->show ();
1284 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1286 /* swap _primary to the copy */
1288 if (rv == _primary) {
1292 /* ..and deselect the one we copied */
1294 rv->set_selected (false);
1297 if (!new_regionviews.empty()) {
1299 /* reflect the fact that we are dragging the copies */
1301 _views = new_regionviews;
1303 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1306 } else if (!_copy && first_move) {
1307 if (_x_constrained && !_brushing) {
1308 _editor->begin_reversible_command (_("fixed time region drag"));
1309 } else if (!_brushing) {
1310 _editor->begin_reversible_command (Operations::region_drag);
1311 } else if (_brushing) {
1312 _editor->begin_reversible_command (Operations::drag_region_brush);
1315 RegionMotionDrag::motion (event, first_move);
1319 RegionMotionDrag::finished (GdkEvent *, bool)
1321 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1322 if (!(*i)->view()) {
1326 if ((*i)->view()->layer_display() == Expanded) {
1327 (*i)->view()->set_layer_display (Stacked);
1333 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1335 RegionMotionDrag::finished (ev, movement_occurred);
1337 if (!movement_occurred) {
1341 if (was_double_click() && !_views.empty()) {
1342 DraggingView dv = _views.front();
1343 dv.view->show_region_editor ();
1350 assert (!_views.empty ());
1352 /* We might have hidden region views so that they weren't visible during the drag
1353 (when they have been reparented). Now everything can be shown again, as region
1354 views are back in their track parent groups.
1356 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1357 i->view->get_canvas_group()->show ();
1360 bool const changed_position = (_last_frame_position != _primary->region()->position());
1361 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1362 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1384 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1388 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1390 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1395 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1396 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1397 uint32_t output_chan = region->n_channels();
1398 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1399 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1401 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1402 TimeAxisView* tav =_editor->axis_view_from_stripable (audio_tracks.front());
1404 tav->set_height (original->current_height());
1406 return dynamic_cast<RouteTimeAxisView*>(tav);
1408 ChanCount one_midi_port (DataType::MIDI, 1);
1409 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1410 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1411 (ARDOUR::Plugin::PresetRecord*) 0,
1412 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1413 TimeAxisView* tav = _editor->axis_view_from_stripable (midi_tracks.front());
1415 tav->set_height (original->current_height());
1417 return dynamic_cast<RouteTimeAxisView*> (tav);
1420 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1426 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta, int32_t const ev_state)
1428 RegionSelection new_views;
1429 PlaylistSet modified_playlists;
1430 RouteTimeAxisView* new_time_axis_view = 0;
1433 /* all changes were made during motion event handlers */
1435 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1439 _editor->commit_reversible_command ();
1443 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1444 PlaylistMapping playlist_mapping;
1446 /* insert the regions into their new playlists */
1447 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1449 RouteTimeAxisView* dest_rtv = 0;
1451 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1457 if (changed_position && !_x_constrained) {
1458 where = i->view->region()->position() - drag_delta;
1460 where = i->view->region()->position();
1463 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1464 /* dragged to drop zone */
1466 PlaylistMapping::iterator pm;
1468 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1469 /* first region from this original playlist: create a new track */
1470 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1471 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1472 dest_rtv = new_time_axis_view;
1474 /* we already created a new track for regions from this playlist, use it */
1475 dest_rtv = pm->second;
1478 /* destination time axis view is the one we dragged to */
1479 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1482 if (dest_rtv != 0) {
1483 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1484 modified_playlists, _editor->get_grid_music_divisions (ev_state));
1486 if (new_view != 0) {
1487 new_views.push_back (new_view);
1491 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1492 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1495 list<DraggingView>::const_iterator next = i;
1501 /* If we've created new regions either by copying or moving
1502 to a new track, we want to replace the old selection with the new ones
1505 if (new_views.size() > 0) {
1506 _editor->selection->set (new_views);
1509 /* write commands for the accumulated diffs for all our modified playlists */
1510 add_stateful_diff_commands_for_playlists (modified_playlists);
1512 _editor->commit_reversible_command ();
1516 RegionMoveDrag::finished_no_copy (
1517 bool const changed_position,
1518 bool const changed_tracks,
1519 framecnt_t const drag_delta,
1520 int32_t const ev_state
1523 RegionSelection new_views;
1524 PlaylistSet modified_playlists;
1525 PlaylistSet frozen_playlists;
1526 set<RouteTimeAxisView*> views_to_update;
1527 RouteTimeAxisView* new_time_axis_view = 0;
1529 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1530 PlaylistMapping playlist_mapping;
1532 std::set<boost::shared_ptr<const Region> > uniq;
1533 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1535 RegionView* rv = i->view;
1536 RouteTimeAxisView* dest_rtv = 0;
1538 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1543 if (uniq.find (rv->region()) != uniq.end()) {
1544 /* prevent duplicate moves when selecting regions from shared playlists */
1548 uniq.insert(rv->region());
1550 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1551 /* dragged to drop zone */
1553 PlaylistMapping::iterator pm;
1555 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1556 /* first region from this original playlist: create a new track */
1557 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1558 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1559 dest_rtv = new_time_axis_view;
1561 /* we already created a new track for regions from this playlist, use it */
1562 dest_rtv = pm->second;
1566 /* destination time axis view is the one we dragged to */
1567 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1572 double const dest_layer = i->layer;
1574 views_to_update.insert (dest_rtv);
1578 if (changed_position && !_x_constrained) {
1579 where = rv->region()->position() - drag_delta;
1581 where = rv->region()->position();
1584 if (changed_tracks) {
1586 /* insert into new playlist */
1588 RegionView* new_view = insert_region_into_playlist (
1589 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1590 modified_playlists, _editor->get_grid_music_divisions (ev_state)
1593 if (new_view == 0) {
1598 new_views.push_back (new_view);
1600 /* remove from old playlist */
1602 /* the region that used to be in the old playlist is not
1603 moved to the new one - we use a copy of it. as a result,
1604 any existing editor for the region should no longer be
1607 rv->hide_region_editor();
1610 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1614 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1616 /* this movement may result in a crossfade being modified, or a layering change,
1617 so we need to get undo data from the playlist as well as the region.
1620 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1622 playlist->clear_changes ();
1625 rv->region()->clear_changes ();
1628 motion on the same track. plonk the previously reparented region
1629 back to its original canvas group (its streamview).
1630 No need to do anything for copies as they are fake regions which will be deleted.
1633 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1634 rv->get_canvas_group()->set_y_position (i->initial_y);
1637 /* just change the model */
1638 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1639 playlist->set_layer (rv->region(), dest_layer);
1642 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1644 r = frozen_playlists.insert (playlist);
1647 playlist->freeze ();
1650 rv->region()->set_position (where, _editor->get_grid_music_divisions (ev_state));
1651 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1654 if (changed_tracks) {
1656 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1657 was selected in all of them, then removing it from a playlist will have removed all
1658 trace of it from _views (i.e. there were N regions selected, we removed 1,
1659 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1660 corresponding regionview, and _views is now empty).
1662 This could have invalidated any and all iterators into _views.
1664 The heuristic we use here is: if the region selection is empty, break out of the loop
1665 here. if the region selection is not empty, then restart the loop because we know that
1666 we must have removed at least the region(view) we've just been working on as well as any
1667 that we processed on previous iterations.
1669 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1670 we can just iterate.
1674 if (_views.empty()) {
1685 /* If we've created new regions either by copying or moving
1686 to a new track, we want to replace the old selection with the new ones
1689 if (new_views.size() > 0) {
1690 _editor->selection->set (new_views);
1693 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1697 /* write commands for the accumulated diffs for all our modified playlists */
1698 add_stateful_diff_commands_for_playlists (modified_playlists);
1699 /* applies to _brushing */
1700 _editor->commit_reversible_command ();
1702 /* We have futzed with the layering of canvas items on our streamviews.
1703 If any region changed layer, this will have resulted in the stream
1704 views being asked to set up their region views, and all will be well.
1705 If not, we might now have badly-ordered region views. Ask the StreamViews
1706 involved to sort themselves out, just in case.
1709 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1710 (*i)->view()->playlist_layered ((*i)->track ());
1714 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1715 * @param region Region to remove.
1716 * @param playlist playlist To remove from.
1717 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1718 * that clear_changes () is only called once per playlist.
1721 RegionMoveDrag::remove_region_from_playlist (
1722 boost::shared_ptr<Region> region,
1723 boost::shared_ptr<Playlist> playlist,
1724 PlaylistSet& modified_playlists
1727 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1730 playlist->clear_changes ();
1733 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1737 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1738 * clearing the playlist's diff history first if necessary.
1739 * @param region Region to insert.
1740 * @param dest_rtv Destination RouteTimeAxisView.
1741 * @param dest_layer Destination layer.
1742 * @param where Destination position.
1743 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1744 * that clear_changes () is only called once per playlist.
1745 * @return New RegionView, or 0 if no insert was performed.
1748 RegionMoveDrag::insert_region_into_playlist (
1749 boost::shared_ptr<Region> region,
1750 RouteTimeAxisView* dest_rtv,
1753 PlaylistSet& modified_playlists,
1754 const int32_t sub_num
1757 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1758 if (!dest_playlist) {
1762 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1763 _new_region_view = 0;
1764 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1766 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1767 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1769 dest_playlist->clear_changes ();
1771 dest_playlist->add_region (region, where, 1.0, false, sub_num);
1773 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1774 dest_playlist->set_layer (region, dest_layer);
1779 assert (_new_region_view);
1781 return _new_region_view;
1785 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1787 _new_region_view = rv;
1791 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1793 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1794 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1796 _editor->session()->add_command (c);
1805 RegionMoveDrag::aborted (bool movement_occurred)
1809 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1810 list<DraggingView>::const_iterator next = i;
1819 RegionMotionDrag::aborted (movement_occurred);
1824 RegionMotionDrag::aborted (bool)
1826 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1828 StreamView* sview = (*i)->view();
1831 if (sview->layer_display() == Expanded) {
1832 sview->set_layer_display (Stacked);
1837 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1838 RegionView* rv = i->view;
1839 TimeAxisView* tv = &(rv->get_time_axis_view ());
1840 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1842 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1843 rv->get_canvas_group()->set_y_position (0);
1845 rv->move (-_total_x_delta, 0);
1846 rv->set_height (rtv->view()->child_height ());
1850 /** @param b true to brush, otherwise false.
1851 * @param c true to make copies of the regions being moved, otherwise false.
1853 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1854 : RegionMotionDrag (e, i, p, v, b)
1856 , _new_region_view (0)
1858 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1861 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1862 if (rtv && rtv->is_track()) {
1863 speed = rtv->track()->speed ();
1866 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1870 RegionMoveDrag::setup_pointer_frame_offset ()
1872 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1875 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1876 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1878 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1880 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1881 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1883 _primary = v->view()->create_region_view (r, false, false);
1885 _primary->get_canvas_group()->show ();
1886 _primary->set_position (pos, 0);
1887 _views.push_back (DraggingView (_primary, this, v));
1889 _last_frame_position = pos;
1891 _item = _primary->get_canvas_group ();
1895 RegionInsertDrag::finished (GdkEvent * event, bool)
1897 int pos = _views.front().time_axis_view;
1898 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1900 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1902 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1903 _primary->get_canvas_group()->set_y_position (0);
1905 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1907 _editor->begin_reversible_command (Operations::insert_region);
1908 playlist->clear_changes ();
1909 playlist->add_region (_primary->region (), _last_frame_position);
1911 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1912 if (Config->get_edit_mode() == Ripple) {
1913 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1916 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1917 _editor->commit_reversible_command ();
1925 RegionInsertDrag::aborted (bool)
1932 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1933 : RegionMoveDrag (e, i, p, v, false, false)
1935 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1938 struct RegionSelectionByPosition {
1939 bool operator() (RegionView*a, RegionView* b) {
1940 return a->region()->position () < b->region()->position();
1945 RegionSpliceDrag::motion (GdkEvent* event, bool)
1947 /* Which trackview is this ? */
1949 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1950 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1952 /* The region motion is only processed if the pointer is over
1956 if (!tv || !tv->is_track()) {
1957 /* To make sure we hide the verbose canvas cursor when the mouse is
1958 not held over an audio track.
1960 _editor->verbose_cursor()->hide ();
1963 _editor->verbose_cursor()->show ();
1968 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1974 RegionSelection copy;
1975 _editor->selection->regions.by_position(copy);
1977 framepos_t const pf = adjusted_current_frame (event);
1979 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1981 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1987 boost::shared_ptr<Playlist> playlist;
1989 if ((playlist = atv->playlist()) == 0) {
1993 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1998 if (pf < (*i)->region()->last_frame() + 1) {
2002 if (pf > (*i)->region()->first_frame()) {
2008 playlist->shuffle ((*i)->region(), dir);
2013 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2015 RegionMoveDrag::finished (event, movement_occurred);
2019 RegionSpliceDrag::aborted (bool)
2029 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2032 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2034 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2035 RegionSelection to_ripple;
2036 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2037 if ((*i)->position() >= where) {
2038 to_ripple.push_back (rtv->view()->find_view(*i));
2042 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2043 if (!exclude.contains (*i)) {
2044 // the selection has already been added to _views
2046 if (drag_in_progress) {
2047 // do the same things that RegionMotionDrag::motion does when
2048 // first_move is true, for the region views that we're adding
2049 // to _views this time
2052 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2053 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2054 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2055 rvg->reparent (_editor->_drag_motion_group);
2057 // we only need to move in the y direction
2058 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2063 _views.push_back (DraggingView (*i, this, tav));
2069 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2072 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2073 // we added all the regions after the selection
2075 std::list<DraggingView>::iterator to_erase = i++;
2076 if (!_editor->selection->regions.contains (to_erase->view)) {
2077 // restore the non-selected regions to their original playlist & positions,
2078 // and then ripple them back by the length of the regions that were dragged away
2079 // do the same things as RegionMotionDrag::aborted
2081 RegionView *rv = to_erase->view;
2082 TimeAxisView* tv = &(rv->get_time_axis_view ());
2083 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2086 // plonk them back onto their own track
2087 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2088 rv->get_canvas_group()->set_y_position (0);
2092 // move the underlying region to match the view
2093 rv->region()->set_position (rv->region()->position() + amount);
2095 // restore the view to match the underlying region's original position
2096 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2099 rv->set_height (rtv->view()->child_height ());
2100 _views.erase (to_erase);
2106 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2108 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2110 return allow_moves_across_tracks;
2118 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2119 : RegionMoveDrag (e, i, p, v, false, false)
2121 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2122 // compute length of selection
2123 RegionSelection selected_regions = _editor->selection->regions;
2124 selection_length = selected_regions.end_frame() - selected_regions.start();
2126 // we'll only allow dragging to another track in ripple mode if all the regions
2127 // being dragged start off on the same track
2128 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2131 exclude = new RegionList;
2132 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2133 exclude->push_back((*i)->region());
2136 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2137 RegionSelection copy;
2138 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2140 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2141 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2143 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2144 // find ripple start point on each applicable playlist
2145 RegionView *first_selected_on_this_track = NULL;
2146 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2147 if ((*i)->region()->playlist() == (*pi)) {
2148 // region is on this playlist - it's the first, because they're sorted
2149 first_selected_on_this_track = *i;
2153 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2154 add_all_after_to_views (
2155 &first_selected_on_this_track->get_time_axis_view(),
2156 first_selected_on_this_track->region()->position(),
2157 selected_regions, false);
2160 if (allow_moves_across_tracks) {
2161 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2169 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2171 /* Which trackview is this ? */
2173 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2174 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2176 /* The region motion is only processed if the pointer is over
2180 if (!tv || !tv->is_track()) {
2181 /* To make sure we hide the verbose canvas cursor when the mouse is
2182 not held over an audiotrack.
2184 _editor->verbose_cursor()->hide ();
2188 framepos_t where = adjusted_current_frame (event);
2189 assert (where >= 0);
2191 double delta = compute_x_delta (event, &after);
2193 framecnt_t amount = _editor->pixel_to_sample (delta);
2195 if (allow_moves_across_tracks) {
2196 // all the originally selected regions were on the same track
2198 framecnt_t adjust = 0;
2199 if (prev_tav && tv != prev_tav) {
2200 // dragged onto a different track
2201 // remove the unselected regions from _views, restore them to their original positions
2202 // and add the regions after the drop point on the new playlist to _views instead.
2203 // undo the effect of rippling the previous playlist, and include the effect of removing
2204 // the dragged region(s) from this track
2206 remove_unselected_from_views (prev_amount, false);
2207 // ripple previous playlist according to the regions that have been removed onto the new playlist
2208 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2211 // move just the selected regions
2212 RegionMoveDrag::motion(event, first_move);
2214 // ensure that the ripple operation on the new playlist inserts selection_length time
2215 adjust = selection_length;
2216 // ripple the new current playlist
2217 tv->playlist()->ripple (where, amount+adjust, exclude);
2219 // add regions after point where drag entered this track to subsequent ripples
2220 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2223 // motion on same track
2224 RegionMoveDrag::motion(event, first_move);
2228 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2229 prev_position = where;
2231 // selection encompasses multiple tracks - just drag
2232 // cross-track drags are forbidden
2233 RegionMoveDrag::motion(event, first_move);
2236 if (!_x_constrained) {
2237 prev_amount += amount;
2240 _last_frame_position = after;
2244 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2246 if (!movement_occurred) {
2250 if (was_double_click() && !_views.empty()) {
2251 DraggingView dv = _views.front();
2252 dv.view->show_region_editor ();
2259 _editor->begin_reversible_command(_("Ripple drag"));
2261 // remove the regions being rippled from the dragging view, updating them to
2262 // their new positions
2263 remove_unselected_from_views (prev_amount, true);
2265 if (allow_moves_across_tracks) {
2267 // if regions were dragged across tracks, we've rippled any later
2268 // regions on the track the regions were dragged off, so we need
2269 // to add the original track to the undo record
2270 orig_tav->playlist()->clear_changes();
2271 vector<Command*> cmds;
2272 orig_tav->playlist()->rdiff (cmds);
2273 _editor->session()->add_commands (cmds);
2275 if (prev_tav && prev_tav != orig_tav) {
2276 prev_tav->playlist()->clear_changes();
2277 vector<Command*> cmds;
2278 prev_tav->playlist()->rdiff (cmds);
2279 _editor->session()->add_commands (cmds);
2282 // selection spanned multiple tracks - all will need adding to undo record
2284 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2285 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2287 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2288 (*pi)->clear_changes();
2289 vector<Command*> cmds;
2290 (*pi)->rdiff (cmds);
2291 _editor->session()->add_commands (cmds);
2295 // other modified playlists are added to undo by RegionMoveDrag::finished()
2296 RegionMoveDrag::finished (event, movement_occurred);
2297 _editor->commit_reversible_command();
2301 RegionRippleDrag::aborted (bool movement_occurred)
2303 RegionMoveDrag::aborted (movement_occurred);
2308 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2310 _view (dynamic_cast<MidiTimeAxisView*> (v))
2312 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2318 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2321 _editor->begin_reversible_command (_("create region"));
2322 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2323 _view->playlist()->freeze ();
2326 framepos_t const f = adjusted_current_frame (event);
2327 if (f < grab_frame()) {
2328 _region->set_initial_position (f);
2331 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2332 so that if this region is duplicated, its duplicate starts on
2333 a snap point rather than 1 frame after a snap point. Otherwise things get
2334 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2335 place snapped notes at the start of the region.
2338 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2339 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2345 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2347 if (!movement_occurred) {
2348 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2350 _view->playlist()->thaw ();
2351 _editor->commit_reversible_command();
2356 RegionCreateDrag::aborted (bool)
2359 _view->playlist()->thaw ();
2365 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2370 , _was_selected (false)
2373 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2377 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2379 Gdk::Cursor* cursor;
2380 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2382 float x_fraction = cnote->mouse_x_fraction ();
2384 if (x_fraction > 0.0 && x_fraction < 0.25) {
2385 cursor = _editor->cursors()->left_side_trim;
2388 cursor = _editor->cursors()->right_side_trim;
2392 Drag::start_grab (event, cursor);
2394 region = &cnote->region_view();
2397 temp = region->snap_to_pixel (cnote->x0 (), true);
2398 _snap_delta = temp - cnote->x0 ();
2402 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2407 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2408 if (ms.size() > 1) {
2409 /* has to be relative, may make no sense otherwise */
2413 if (!(_was_selected = cnote->selected())) {
2415 /* tertiary-click means extend selection - we'll do that on button release,
2416 so don't add it here, because otherwise we make it hard to figure
2417 out the "extend-to" range.
2420 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2423 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2426 region->note_selected (cnote, true);
2428 _editor->get_selection().clear_points();
2429 region->unique_select (cnote);
2436 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2438 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2440 _editor->begin_reversible_command (_("resize notes"));
2442 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2443 MidiRegionSelection::iterator next;
2446 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2448 mrv->begin_resizing (at_front);
2454 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2455 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2457 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2461 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2463 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2464 if (_editor->snap_mode () != SnapOff) {
2468 if (_editor->snap_mode () == SnapOff) {
2470 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2471 if (apply_snap_delta) {
2477 if (apply_snap_delta) {
2481 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2487 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2489 if (!movement_occurred) {
2490 /* no motion - select note */
2491 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2492 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2493 _editor->current_mouse_mode() == Editing::MouseDraw) {
2495 bool changed = false;
2497 if (_was_selected) {
2498 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2500 region->note_deselected (cnote);
2503 _editor->get_selection().clear_points();
2504 region->unique_select (cnote);
2508 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2509 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2511 if (!extend && !add && region->selection_size() > 1) {
2512 _editor->get_selection().clear_points();
2513 region->unique_select (cnote);
2515 } else if (extend) {
2516 region->note_selected (cnote, true, true);
2519 /* it was added during button press */
2525 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2526 _editor->commit_reversible_selection_op();
2533 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2534 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2535 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2537 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2540 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2542 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2543 if (_editor->snap_mode () != SnapOff) {
2547 if (_editor->snap_mode () == SnapOff) {
2549 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2550 if (apply_snap_delta) {
2556 if (apply_snap_delta) {
2560 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2564 _editor->commit_reversible_command ();
2568 NoteResizeDrag::aborted (bool)
2570 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2571 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2572 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2574 mrv->abort_resizing ();
2579 AVDraggingView::AVDraggingView (RegionView* v)
2582 initial_position = v->region()->position ();
2585 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2588 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2591 TrackViewList empty;
2593 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2594 std::list<RegionView*> views = rs.by_layer();
2597 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2598 RegionView* rv = (*i);
2599 if (!rv->region()->video_locked()) {
2602 if (rv->region()->locked()) {
2605 _views.push_back (AVDraggingView (rv));
2610 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2612 Drag::start_grab (event);
2613 if (_editor->session() == 0) {
2617 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2623 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2627 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2628 _max_backwards_drag = (
2629 ARDOUR_UI::instance()->video_timeline->get_duration()
2630 + ARDOUR_UI::instance()->video_timeline->get_offset()
2631 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2634 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2635 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2636 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2639 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2642 Timecode::Time timecode;
2643 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2644 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);
2645 show_verbose_cursor_text (buf);
2649 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2651 if (_editor->session() == 0) {
2654 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2658 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2662 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2663 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2665 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2666 dt = - _max_backwards_drag;
2669 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2670 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2672 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2673 RegionView* rv = i->view;
2674 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2677 rv->region()->clear_changes ();
2678 rv->region()->suspend_property_changes();
2680 rv->region()->set_position(i->initial_position + dt);
2681 rv->region_changed(ARDOUR::Properties::position);
2684 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2685 Timecode::Time timecode;
2686 Timecode::Time timediff;
2688 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2689 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2690 snprintf (buf, sizeof (buf),
2691 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2692 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2693 , _("Video Start:"),
2694 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2696 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2698 show_verbose_cursor_text (buf);
2702 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2704 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2711 if (!movement_occurred || ! _editor->session()) {
2715 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2717 _editor->begin_reversible_command (_("Move Video"));
2719 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2720 ARDOUR_UI::instance()->video_timeline->save_undo();
2721 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2722 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2724 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2725 i->view->drag_end();
2726 i->view->region()->resume_property_changes ();
2728 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2731 _editor->session()->maybe_update_session_range(
2732 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2733 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2737 _editor->commit_reversible_command ();
2741 VideoTimeLineDrag::aborted (bool)
2743 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2746 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2747 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2749 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2750 i->view->region()->resume_property_changes ();
2751 i->view->region()->set_position(i->initial_position);
2755 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2756 : RegionDrag (e, i, p, v)
2757 , _operation (StartTrim)
2758 , _preserve_fade_anchor (preserve_fade_anchor)
2759 , _jump_position_when_done (false)
2761 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2765 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2768 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2769 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2771 if (tv && tv->is_track()) {
2772 speed = tv->track()->speed();
2775 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2776 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2777 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2779 framepos_t const pf = adjusted_current_frame (event);
2780 setup_snap_delta (region_start);
2782 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2783 /* Move the contents of the region around without changing the region bounds */
2784 _operation = ContentsTrim;
2785 Drag::start_grab (event, _editor->cursors()->trimmer);
2787 /* These will get overridden for a point trim.*/
2788 if (pf < (region_start + region_length/2)) {
2789 /* closer to front */
2790 _operation = StartTrim;
2791 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2792 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2794 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2798 _operation = EndTrim;
2799 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2800 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2802 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2806 /* jump trim disabled for now
2807 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2808 _jump_position_when_done = true;
2812 switch (_operation) {
2814 show_verbose_cursor_time (region_start);
2817 show_verbose_cursor_duration (region_start, region_end);
2820 show_verbose_cursor_time (pf);
2824 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2825 i->view->region()->suspend_property_changes ();
2830 TrimDrag::motion (GdkEvent* event, bool first_move)
2832 RegionView* rv = _primary;
2835 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2836 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2837 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2838 frameoffset_t frame_delta = 0;
2840 if (tv && tv->is_track()) {
2841 speed = tv->track()->speed();
2843 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2844 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2850 switch (_operation) {
2852 trim_type = "Region start trim";
2855 trim_type = "Region end trim";
2858 trim_type = "Region content trim";
2865 _editor->begin_reversible_command (trim_type);
2867 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2868 RegionView* rv = i->view;
2869 rv->region()->playlist()->clear_owned_changes ();
2871 if (_operation == StartTrim) {
2872 rv->trim_front_starting ();
2875 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2878 arv->temporarily_hide_envelope ();
2882 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2883 insert_result = _editor->motion_frozen_playlists.insert (pl);
2885 if (insert_result.second) {
2891 bool non_overlap_trim = false;
2893 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2894 non_overlap_trim = true;
2897 /* contstrain trim to fade length */
2898 if (_preserve_fade_anchor) {
2899 switch (_operation) {
2901 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2902 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2904 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2905 if (ar->locked()) continue;
2906 framecnt_t len = ar->fade_in()->back()->when;
2907 if (len < dt) dt = min(dt, len);
2911 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2912 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2914 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2915 if (ar->locked()) continue;
2916 framecnt_t len = ar->fade_out()->back()->when;
2917 if (len < -dt) dt = max(dt, -len);
2925 int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
2927 if (_editor->snap_mode() == Editing::SnapMagnetic) {
2928 const framepos_t presnap = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, false);
2930 if (presnap == adj_frame) {
2935 switch (_operation) {
2937 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2938 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2941 if (changed && _preserve_fade_anchor) {
2942 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2944 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2945 framecnt_t len = ar->fade_in()->back()->when;
2946 framecnt_t diff = ar->first_frame() - i->initial_position;
2947 framepos_t new_length = len - diff;
2948 i->anchored_fade_length = min (ar->length(), new_length);
2949 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2950 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2957 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2958 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, divisions);
2959 if (changed && _preserve_fade_anchor) {
2960 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2962 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2963 framecnt_t len = ar->fade_out()->back()->when;
2964 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2965 framepos_t new_length = len + diff;
2966 i->anchored_fade_length = min (ar->length(), new_length);
2967 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2968 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2976 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2978 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2979 i->view->move_contents (frame_delta);
2985 switch (_operation) {
2987 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2990 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2993 // show_verbose_cursor_time (frame_delta);
2999 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3001 if (movement_occurred) {
3002 motion (event, false);
3004 if (_operation == StartTrim) {
3005 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3007 /* This must happen before the region's StatefulDiffCommand is created, as it may
3008 `correct' (ahem) the region's _start from being negative to being zero. It
3009 needs to be zero in the undo record.
3011 i->view->trim_front_ending ();
3013 if (_preserve_fade_anchor) {
3014 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3016 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3017 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3018 ar->set_fade_in_length(i->anchored_fade_length);
3019 ar->set_fade_in_active(true);
3022 if (_jump_position_when_done) {
3023 i->view->region()->set_position (i->initial_position);
3026 } else if (_operation == EndTrim) {
3027 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3028 if (_preserve_fade_anchor) {
3029 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3031 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3032 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3033 ar->set_fade_out_length(i->anchored_fade_length);
3034 ar->set_fade_out_active(true);
3037 if (_jump_position_when_done) {
3038 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3043 if (!_views.empty()) {
3044 if (_operation == StartTrim) {
3045 _editor->maybe_locate_with_edit_preroll(
3046 _views.begin()->view->region()->position());
3048 if (_operation == EndTrim) {
3049 _editor->maybe_locate_with_edit_preroll(
3050 _views.begin()->view->region()->position() +
3051 _views.begin()->view->region()->length());
3055 if (!_editor->selection->selected (_primary)) {
3056 _primary->thaw_after_trim ();
3059 set<boost::shared_ptr<Playlist> > diffed_playlists;
3061 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3062 i->view->thaw_after_trim ();
3063 i->view->enable_display (true);
3065 /* Trimming one region may affect others on the playlist, so we need
3066 to get undo Commands from the whole playlist rather than just the
3067 region. Use diffed_playlists to make sure we don't diff a given
3068 playlist more than once.
3070 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3071 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3072 vector<Command*> cmds;
3074 _editor->session()->add_commands (cmds);
3075 diffed_playlists.insert (p);
3080 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3084 _editor->motion_frozen_playlists.clear ();
3085 _editor->commit_reversible_command();
3088 /* no mouse movement */
3089 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3090 _editor->point_trim (event, adjusted_current_frame (event));
3094 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3095 i->view->region()->resume_property_changes ();
3100 TrimDrag::aborted (bool movement_occurred)
3102 /* Our motion method is changing model state, so use the Undo system
3103 to cancel. Perhaps not ideal, as this will leave an Undo point
3104 behind which may be slightly odd from the user's point of view.
3108 finished (&ev, true);
3110 if (movement_occurred) {
3111 _editor->session()->undo (1);
3114 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3115 i->view->region()->resume_property_changes ();
3120 TrimDrag::setup_pointer_frame_offset ()
3122 list<DraggingView>::iterator i = _views.begin ();
3123 while (i != _views.end() && i->view != _primary) {
3127 if (i == _views.end()) {
3131 switch (_operation) {
3133 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3136 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3143 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3146 , _old_snap_type (e->snap_type())
3147 , _old_snap_mode (e->snap_mode())
3150 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3151 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3153 _real_section = &_marker->meter();
3158 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3160 Drag::start_grab (event, cursor);
3161 show_verbose_cursor_time (adjusted_current_frame(event));
3165 MeterMarkerDrag::setup_pointer_frame_offset ()
3167 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3171 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3174 // create a dummy marker to catch events, then hide it.
3177 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3179 _marker = new MeterMarker (
3181 *_editor->meter_group,
3182 UIConfiguration::instance().color ("meter marker"),
3184 *new MeterSection (_marker->meter())
3187 /* use the new marker for the grab */
3188 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3191 TempoMap& map (_editor->session()->tempo_map());
3192 /* get current state */
3193 before_state = &map.get_state();
3196 _editor->begin_reversible_command (_("move meter mark"));
3198 _editor->begin_reversible_command (_("copy meter mark"));
3200 Timecode::BBT_Time bbt = _real_section->bbt();
3202 /* we can't add a meter where one currently exists */
3203 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3208 const double beat = map.beat_at_bbt (bbt);
3209 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3210 , beat, bbt, _real_section->position_lock_style());
3211 if (!_real_section) {
3217 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3218 if (_real_section->position_lock_style() != AudioTime) {
3219 _editor->set_snap_to (SnapToBar);
3220 _editor->set_snap_mode (SnapNormal);
3224 framepos_t pf = adjusted_current_frame (event);
3226 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3227 /* never snap to music for audio locked */
3228 pf = adjusted_current_frame (event, false);
3231 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3233 /* fake marker meeds to stay under the mouse, unlike the real one. */
3234 _marker->set_position (adjusted_current_frame (event, false));
3236 show_verbose_cursor_time (_real_section->frame());
3240 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3242 if (!movement_occurred) {
3243 if (was_double_click()) {
3244 _editor->edit_meter_marker (*_marker);
3249 /* reinstate old snap setting */
3250 _editor->set_snap_to (_old_snap_type);
3251 _editor->set_snap_mode (_old_snap_mode);
3253 TempoMap& map (_editor->session()->tempo_map());
3255 XMLNode &after = map.get_state();
3256 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3257 _editor->commit_reversible_command ();
3259 // delete the dummy marker we used for visual representation while moving.
3260 // a new visual marker will show up automatically.
3265 MeterMarkerDrag::aborted (bool moved)
3267 _marker->set_position (_marker->meter().frame ());
3269 /* reinstate old snap setting */
3270 _editor->set_snap_to (_old_snap_type);
3271 _editor->set_snap_mode (_old_snap_mode);
3273 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3274 // delete the dummy marker we used for visual representation while moving.
3275 // a new visual marker will show up automatically.
3280 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3286 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3288 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3289 _real_section = &_marker->tempo();
3290 _movable = _real_section->movable();
3291 _grab_bpm = _real_section->note_types_per_minute();
3296 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3298 Drag::start_grab (event, cursor);
3299 if (!_real_section->active()) {
3300 show_verbose_cursor_text (_("inactive"));
3302 show_verbose_cursor_time (adjusted_current_frame (event));
3307 TempoMarkerDrag::setup_pointer_frame_offset ()
3309 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3313 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3315 if (!_real_section->active()) {
3321 // mvc drag - create a dummy marker to catch events, hide it.
3324 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3326 TempoSection section (_marker->tempo());
3328 _marker = new TempoMarker (
3330 *_editor->tempo_group,
3331 UIConfiguration::instance().color ("tempo marker"),
3333 *new TempoSection (_marker->tempo())
3336 /* use the new marker for the grab */
3337 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3340 TempoMap& map (_editor->session()->tempo_map());
3341 /* get current state */
3342 before_state = &map.get_state();
3345 _editor->begin_reversible_command (_("move tempo mark"));
3348 const Tempo tempo (_marker->tempo());
3349 const framepos_t frame = adjusted_current_frame (event) + 1;
3350 const TempoSection::Type type = _real_section->type();
3352 _editor->begin_reversible_command (_("copy tempo mark"));
3354 if (_real_section->position_lock_style() == MusicTime) {
3355 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3356 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3358 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3361 if (!_real_section) {
3369 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3370 /* use vertical movement to alter tempo .. should be log */
3371 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3373 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3375 show_verbose_cursor_text (strs.str());
3377 } else if (_movable && !_real_section->locked_to_meter()) {
3380 if (_editor->snap_musical()) {
3381 /* we can't snap to a grid that we are about to move.
3382 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3384 pf = adjusted_current_frame (event, false);
3386 pf = adjusted_current_frame (event);
3389 TempoMap& map (_editor->session()->tempo_map());
3391 /* snap to beat is 1, snap to bar is -1 (sorry) */
3392 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3394 map.gui_move_tempo (_real_section, pf, sub_num);
3396 show_verbose_cursor_time (_real_section->frame());
3398 _marker->set_position (adjusted_current_frame (event, false));
3402 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3404 if (!_real_section->active()) {
3407 if (!movement_occurred) {
3408 if (was_double_click()) {
3409 _editor->edit_tempo_marker (*_marker);
3414 TempoMap& map (_editor->session()->tempo_map());
3416 XMLNode &after = map.get_state();
3417 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3418 _editor->commit_reversible_command ();
3420 // delete the dummy marker we used for visual representation while moving.
3421 // a new visual marker will show up automatically.
3426 TempoMarkerDrag::aborted (bool moved)
3428 _marker->set_position (_marker->tempo().frame());
3430 TempoMap& map (_editor->session()->tempo_map());
3431 map.set_state (*before_state, Stateful::current_state_version);
3432 // delete the dummy (hidden) marker we used for events while moving.
3437 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3443 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3448 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3450 Drag::start_grab (event, cursor);
3451 TempoMap& map (_editor->session()->tempo_map());
3452 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3455 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3456 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3457 show_verbose_cursor_text (sstr.str());
3458 finished (event, false);
3462 BBTRulerDrag::setup_pointer_frame_offset ()
3464 TempoMap& map (_editor->session()->tempo_map());
3465 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3466 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3469 if (divisions > 0) {
3470 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3472 /* while it makes some sense for the user to determine the division to 'grab',
3473 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3474 and the result over steep tempo curves. Use sixteenths.
3476 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3479 _grab_qn = map.quarter_note_at_beat (beat);
3481 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3486 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3488 TempoMap& map (_editor->session()->tempo_map());
3491 /* get current state */
3492 before_state = &map.get_state();
3493 _editor->begin_reversible_command (_("dilate tempo"));
3498 if (_editor->snap_musical()) {
3499 pf = adjusted_current_frame (event, false);
3501 pf = adjusted_current_frame (event);
3504 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3505 /* adjust previous tempo to match pointer frame */
3506 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3509 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3510 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3511 show_verbose_cursor_text (sstr.str());
3515 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3517 if (!movement_occurred) {
3521 TempoMap& map (_editor->session()->tempo_map());
3523 XMLNode &after = map.get_state();
3524 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3525 _editor->commit_reversible_command ();
3529 BBTRulerDrag::aborted (bool moved)
3532 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3537 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3538 : Drag (e, &c.track_canvas_item(), false)
3543 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3546 /** Do all the things we do when dragging the playhead to make it look as though
3547 * we have located, without actually doing the locate (because that would cause
3548 * the diskstream buffers to be refilled, which is too slow).
3551 CursorDrag::fake_locate (framepos_t t)
3553 if (_editor->session () == 0) {
3557 _editor->playhead_cursor->set_position (t);
3559 Session* s = _editor->session ();
3560 if (s->timecode_transmission_suspended ()) {
3561 framepos_t const f = _editor->playhead_cursor->current_frame ();
3562 /* This is asynchronous so it will be sent "now"
3564 s->send_mmc_locate (f);
3565 /* These are synchronous and will be sent during the next
3568 s->queue_full_time_code ();
3569 s->queue_song_position_pointer ();
3572 show_verbose_cursor_time (t);
3573 _editor->UpdateAllTransportClocks (t);
3577 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3579 Drag::start_grab (event, c);
3580 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3582 _grab_zoom = _editor->samples_per_pixel;
3584 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3586 _editor->snap_to_with_modifier (where, event);
3588 _editor->_dragging_playhead = true;
3590 Session* s = _editor->session ();
3592 /* grab the track canvas item as well */
3594 _cursor.track_canvas_item().grab();
3597 if (_was_rolling && _stop) {
3601 if (s->is_auditioning()) {
3602 s->cancel_audition ();
3606 if (AudioEngine::instance()->connected()) {
3608 /* do this only if we're the engine is connected
3609 * because otherwise this request will never be
3610 * serviced and we'll busy wait forever. likewise,
3611 * notice if we are disconnected while waiting for the
3612 * request to be serviced.
3615 s->request_suspend_timecode_transmission ();
3616 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3617 /* twiddle our thumbs */
3622 fake_locate (where - snap_delta (event->button.state));
3626 CursorDrag::motion (GdkEvent* event, bool)
3628 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3629 _editor->snap_to_with_modifier (where, event);
3630 if (where != last_pointer_frame()) {
3631 fake_locate (where - snap_delta (event->button.state));
3636 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3638 _editor->_dragging_playhead = false;
3640 _cursor.track_canvas_item().ungrab();
3642 if (!movement_occurred && _stop) {
3646 motion (event, false);
3648 Session* s = _editor->session ();
3650 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3651 _editor->_pending_locate_request = true;
3652 s->request_resume_timecode_transmission ();
3657 CursorDrag::aborted (bool)
3659 _cursor.track_canvas_item().ungrab();
3661 if (_editor->_dragging_playhead) {
3662 _editor->session()->request_resume_timecode_transmission ();
3663 _editor->_dragging_playhead = false;
3666 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3669 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3670 : RegionDrag (e, i, p, v)
3672 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3676 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3678 Drag::start_grab (event, cursor);
3680 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3681 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3682 setup_snap_delta (r->position ());
3684 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3688 FadeInDrag::setup_pointer_frame_offset ()
3690 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3691 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3692 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3696 FadeInDrag::motion (GdkEvent* event, bool)
3698 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 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3716 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3722 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3725 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3729 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3731 if (!movement_occurred) {
3735 framecnt_t fade_length;
3736 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3737 _editor->snap_to_with_modifier (pos, event);
3738 pos -= snap_delta (event->button.state);
3740 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3742 if (pos < (region->position() + 64)) {
3743 fade_length = 64; // this should be a minimum defined somewhere
3744 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3745 fade_length = region->length() - region->fade_out()->back()->when - 1;
3747 fade_length = pos - region->position();
3750 bool in_command = false;
3752 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3754 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3760 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3761 XMLNode &before = alist->get_state();
3763 tmp->audio_region()->set_fade_in_length (fade_length);
3764 tmp->audio_region()->set_fade_in_active (true);
3767 _editor->begin_reversible_command (_("change fade in length"));
3770 XMLNode &after = alist->get_state();
3771 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3775 _editor->commit_reversible_command ();
3780 FadeInDrag::aborted (bool)
3782 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3783 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3789 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3793 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3794 : RegionDrag (e, i, p, v)
3796 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3800 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3802 Drag::start_grab (event, cursor);
3804 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3805 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3806 setup_snap_delta (r->last_frame ());
3808 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3812 FadeOutDrag::setup_pointer_frame_offset ()
3814 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3815 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3816 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3820 FadeOutDrag::motion (GdkEvent* event, bool)
3822 framecnt_t fade_length;
3824 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3825 _editor->snap_to_with_modifier (pos, event);
3826 pos -= snap_delta (event->button.state);
3828 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3830 if (pos > (region->last_frame() - 64)) {
3831 fade_length = 64; // this should really be a minimum fade defined somewhere
3832 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3833 fade_length = region->length() - region->fade_in()->back()->when - 1;
3835 fade_length = region->last_frame() - pos;
3838 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3840 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3846 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3849 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3853 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3855 if (!movement_occurred) {
3859 framecnt_t fade_length;
3861 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3862 _editor->snap_to_with_modifier (pos, event);
3863 pos -= snap_delta (event->button.state);
3865 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3867 if (pos > (region->last_frame() - 64)) {
3868 fade_length = 64; // this should really be a minimum fade defined somewhere
3869 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3870 fade_length = region->length() - region->fade_in()->back()->when - 1;
3872 fade_length = region->last_frame() - pos;
3875 bool in_command = false;
3877 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3879 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3885 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3886 XMLNode &before = alist->get_state();
3888 tmp->audio_region()->set_fade_out_length (fade_length);
3889 tmp->audio_region()->set_fade_out_active (true);
3892 _editor->begin_reversible_command (_("change fade out length"));
3895 XMLNode &after = alist->get_state();
3896 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3900 _editor->commit_reversible_command ();
3905 FadeOutDrag::aborted (bool)
3907 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3908 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3914 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3918 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3920 , _selection_changed (false)
3922 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3923 Gtk::Window* toplevel = _editor->current_toplevel();
3924 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3928 _points.push_back (ArdourCanvas::Duple (0, 0));
3930 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3933 MarkerDrag::~MarkerDrag ()
3935 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3940 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3942 location = new Location (*l);
3943 markers.push_back (m);
3948 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3950 Drag::start_grab (event, cursor);
3954 Location *location = _editor->find_location_from_marker (_marker, is_start);
3955 _editor->_dragging_edit_point = true;
3957 update_item (location);
3959 // _drag_line->show();
3960 // _line->raise_to_top();
3963 show_verbose_cursor_time (location->start());
3965 show_verbose_cursor_time (location->end());
3967 setup_snap_delta (is_start ? location->start() : location->end());
3969 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3972 case Selection::Toggle:
3973 /* we toggle on the button release */
3975 case Selection::Set:
3976 if (!_editor->selection->selected (_marker)) {
3977 _editor->selection->set (_marker);
3978 _selection_changed = true;
3981 case Selection::Extend:
3983 Locations::LocationList ll;
3984 list<ArdourMarker*> to_add;
3986 _editor->selection->markers.range (s, e);
3987 s = min (_marker->position(), s);
3988 e = max (_marker->position(), e);
3991 if (e < max_framepos) {
3994 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3995 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3996 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3999 to_add.push_back (lm->start);
4002 to_add.push_back (lm->end);
4006 if (!to_add.empty()) {
4007 _editor->selection->add (to_add);
4008 _selection_changed = true;
4012 case Selection::Add:
4013 _editor->selection->add (_marker);
4014 _selection_changed = true;
4019 /* Set up copies for us to manipulate during the drag
4022 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4024 Location* l = _editor->find_location_from_marker (*i, is_start);
4031 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4033 /* range: check that the other end of the range isn't
4036 CopiedLocationInfo::iterator x;
4037 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4038 if (*(*x).location == *l) {
4042 if (x == _copied_locations.end()) {
4043 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4045 (*x).markers.push_back (*i);
4046 (*x).move_both = true;
4054 MarkerDrag::setup_pointer_frame_offset ()
4057 Location *location = _editor->find_location_from_marker (_marker, is_start);
4058 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4062 MarkerDrag::motion (GdkEvent* event, bool)
4064 framecnt_t f_delta = 0;
4066 bool move_both = false;
4067 Location *real_location;
4068 Location *copy_location = 0;
4069 framecnt_t const sd = snap_delta (event->button.state);
4071 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4072 framepos_t next = newframe;
4074 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4078 CopiedLocationInfo::iterator x;
4080 /* find the marker we're dragging, and compute the delta */
4082 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4084 copy_location = (*x).location;
4086 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4088 /* this marker is represented by this
4089 * CopiedLocationMarkerInfo
4092 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4097 if (real_location->is_mark()) {
4098 f_delta = newframe - copy_location->start();
4102 switch (_marker->type()) {
4103 case ArdourMarker::SessionStart:
4104 case ArdourMarker::RangeStart:
4105 case ArdourMarker::LoopStart:
4106 case ArdourMarker::PunchIn:
4107 f_delta = newframe - copy_location->start();
4110 case ArdourMarker::SessionEnd:
4111 case ArdourMarker::RangeEnd:
4112 case ArdourMarker::LoopEnd:
4113 case ArdourMarker::PunchOut:
4114 f_delta = newframe - copy_location->end();
4117 /* what kind of marker is this ? */
4126 if (x == _copied_locations.end()) {
4127 /* hmm, impossible - we didn't find the dragged marker */
4131 /* now move them all */
4133 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4135 copy_location = x->location;
4137 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4141 if (real_location->locked()) {
4145 if (copy_location->is_mark()) {
4149 copy_location->set_start (copy_location->start() + f_delta);
4153 framepos_t new_start = copy_location->start() + f_delta;
4154 framepos_t new_end = copy_location->end() + f_delta;
4156 if (is_start) { // start-of-range marker
4158 if (move_both || (*x).move_both) {
4159 copy_location->set_start (new_start);
4160 copy_location->set_end (new_end);
4161 } else if (new_start < copy_location->end()) {
4162 copy_location->set_start (new_start);
4163 } else if (newframe > 0) {
4164 //_editor->snap_to (next, RoundUpAlways, true);
4165 copy_location->set_end (next);
4166 copy_location->set_start (newframe);
4169 } else { // end marker
4171 if (move_both || (*x).move_both) {
4172 copy_location->set_end (new_end);
4173 copy_location->set_start (new_start);
4174 } else if (new_end > copy_location->start()) {
4175 copy_location->set_end (new_end);
4176 } else if (newframe > 0) {
4177 //_editor->snap_to (next, RoundDownAlways, true);
4178 copy_location->set_start (next);
4179 copy_location->set_end (newframe);
4184 update_item (copy_location);
4186 /* now lookup the actual GUI items used to display this
4187 * location and move them to wherever the copy of the location
4188 * is now. This means that the logic in ARDOUR::Location is
4189 * still enforced, even though we are not (yet) modifying
4190 * the real Location itself.
4193 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4196 lm->set_position (copy_location->start(), copy_location->end());
4201 assert (!_copied_locations.empty());
4203 show_verbose_cursor_time (newframe);
4207 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4209 if (!movement_occurred) {
4211 if (was_double_click()) {
4212 _editor->rename_marker (_marker);
4216 /* just a click, do nothing but finish
4217 off the selection process
4220 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4222 case Selection::Set:
4223 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4224 _editor->selection->set (_marker);
4225 _selection_changed = true;
4229 case Selection::Toggle:
4230 /* we toggle on the button release, click only */
4231 _editor->selection->toggle (_marker);
4232 _selection_changed = true;
4236 case Selection::Extend:
4237 case Selection::Add:
4241 if (_selection_changed) {
4242 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4243 _editor->commit_reversible_selection_op();
4249 _editor->_dragging_edit_point = false;
4251 XMLNode &before = _editor->session()->locations()->get_state();
4252 bool in_command = false;
4254 MarkerSelection::iterator i;
4255 CopiedLocationInfo::iterator x;
4258 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4259 x != _copied_locations.end() && i != _editor->selection->markers.end();
4262 Location * location = _editor->find_location_from_marker (*i, is_start);
4266 if (location->locked()) {
4270 _editor->begin_reversible_command ( _("move marker") );
4273 if (location->is_mark()) {
4274 location->set_start (((*x).location)->start());
4276 location->set (((*x).location)->start(), ((*x).location)->end());
4279 if (location->is_session_range()) {
4280 _editor->session()->set_end_is_free (false);
4286 XMLNode &after = _editor->session()->locations()->get_state();
4287 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4288 _editor->commit_reversible_command ();
4293 MarkerDrag::aborted (bool movement_occurred)
4295 if (!movement_occurred) {
4299 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4301 /* move all markers to their original location */
4304 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4307 Location * location = _editor->find_location_from_marker (*m, is_start);
4310 (*m)->set_position (is_start ? location->start() : location->end());
4317 MarkerDrag::update_item (Location*)
4322 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4324 , _fixed_grab_x (0.0)
4325 , _fixed_grab_y (0.0)
4326 , _cumulative_x_drag (0.0)
4327 , _cumulative_y_drag (0.0)
4331 if (_zero_gain_fraction < 0.0) {
4332 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4335 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4337 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4343 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4345 Drag::start_grab (event, _editor->cursors()->fader);
4347 // start the grab at the center of the control point so
4348 // the point doesn't 'jump' to the mouse after the first drag
4349 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4350 _fixed_grab_y = _point->get_y();
4352 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4353 setup_snap_delta (pos);
4355 float const fraction = 1 - (_point->get_y() / _point->line().height());
4356 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4358 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4360 if (!_point->can_slide ()) {
4361 _x_constrained = true;
4366 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4368 double dx = _drags->current_pointer_x() - last_pointer_x();
4369 double dy = current_pointer_y() - last_pointer_y();
4370 bool need_snap = true;
4372 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4378 /* coordinate in pixels relative to the start of the region (for region-based automation)
4379 or track (for track-based automation) */
4380 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4381 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4383 // calculate zero crossing point. back off by .01 to stay on the
4384 // positive side of zero
4385 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4387 if (_x_constrained) {
4390 if (_y_constrained) {
4394 _cumulative_x_drag = cx - _fixed_grab_x;
4395 _cumulative_y_drag = cy - _fixed_grab_y;
4399 cy = min ((double) _point->line().height(), cy);
4401 // make sure we hit zero when passing through
4402 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4406 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4407 if (!_x_constrained && need_snap) {
4408 _editor->snap_to_with_modifier (cx_frames, event);
4411 cx_frames -= snap_delta (event->button.state);
4412 cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset());
4414 float const fraction = 1.0 - (cy / _point->line().height());
4417 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4418 _editor->begin_reversible_command (_("automation event move"));
4419 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4421 pair<double, float> result;
4422 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4424 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4428 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4430 if (!movement_occurred) {
4433 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4434 _editor->reset_point_selection ();
4438 _point->line().end_drag (_pushing, _final_index);
4439 _editor->commit_reversible_command ();
4444 ControlPointDrag::aborted (bool)
4446 _point->line().reset ();
4450 ControlPointDrag::active (Editing::MouseMode m)
4452 if (m == Editing::MouseDraw) {
4453 /* always active in mouse draw */
4457 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4458 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4461 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4464 , _fixed_grab_x (0.0)
4465 , _fixed_grab_y (0.0)
4466 , _cumulative_y_drag (0)
4470 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4474 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4476 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4479 _item = &_line->grab_item ();
4481 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4482 origin, and ditto for y.
4485 double mx = event->button.x;
4486 double my = event->button.y;
4488 _line->grab_item().canvas_to_item (mx, my);
4490 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4492 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4493 /* no adjacent points */
4497 Drag::start_grab (event, _editor->cursors()->fader);
4499 /* store grab start in item frame */
4500 double const bx = _line->nth (_before)->get_x();
4501 double const ax = _line->nth (_after)->get_x();
4502 double const click_ratio = (ax - mx) / (ax - bx);
4504 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4509 double fraction = 1.0 - (cy / _line->height());
4511 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4515 LineDrag::motion (GdkEvent* event, bool first_move)
4517 double dy = current_pointer_y() - last_pointer_y();
4519 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4523 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4525 _cumulative_y_drag = cy - _fixed_grab_y;
4528 cy = min ((double) _line->height(), cy);
4530 double const fraction = 1.0 - (cy / _line->height());
4534 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4536 _editor->begin_reversible_command (_("automation range move"));
4537 _line->start_drag_line (_before, _after, initial_fraction);
4540 /* we are ignoring x position for this drag, so we can just pass in anything */
4541 pair<double, float> result;
4543 result = _line->drag_motion (0, fraction, true, false, ignored);
4544 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4548 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4550 if (movement_occurred) {
4551 motion (event, false);
4552 _line->end_drag (false, 0);
4553 _editor->commit_reversible_command ();
4555 /* add a new control point on the line */
4557 AutomationTimeAxisView* atv;
4559 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4560 framepos_t where = grab_frame ();
4563 double cy = _fixed_grab_y;
4565 _line->grab_item().item_to_canvas (cx, cy);
4567 atv->add_automation_event (event, where, cy, false);
4568 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4569 AudioRegionView* arv;
4571 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4572 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4579 LineDrag::aborted (bool)
4584 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4588 _region_view_grab_x (0.0),
4589 _cumulative_x_drag (0),
4593 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4597 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4599 Drag::start_grab (event);
4601 _line = reinterpret_cast<Line*> (_item);
4604 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4606 double cx = event->button.x;
4607 double cy = event->button.y;
4609 _item->parent()->canvas_to_item (cx, cy);
4611 /* store grab start in parent frame */
4612 _region_view_grab_x = cx;
4614 _before = *(float*) _item->get_data ("position");
4616 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4618 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4622 FeatureLineDrag::motion (GdkEvent*, bool)
4624 double dx = _drags->current_pointer_x() - last_pointer_x();
4626 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4628 _cumulative_x_drag += dx;
4630 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4639 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4641 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4643 float *pos = new float;
4646 _line->set_data ("position", pos);
4652 FeatureLineDrag::finished (GdkEvent*, bool)
4654 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4655 _arv->update_transient(_before, _before);
4659 FeatureLineDrag::aborted (bool)
4664 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4666 , _vertical_only (false)
4668 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4672 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4674 Drag::start_grab (event);
4675 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4679 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4686 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4688 framepos_t grab = grab_frame ();
4689 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4690 _editor->snap_to_with_modifier (grab, event);
4692 grab = raw_grab_frame ();
4695 /* base start and end on initial click position */
4705 if (current_pointer_y() < grab_y()) {
4706 y1 = current_pointer_y();
4709 y2 = current_pointer_y();
4713 if (start != end || y1 != y2) {
4715 double x1 = _editor->sample_to_pixel (start);
4716 double x2 = _editor->sample_to_pixel (end);
4717 const double min_dimension = 2.0;
4719 if (_vertical_only) {
4720 /* fixed 10 pixel width */
4724 x2 = min (x1 - min_dimension, x2);
4726 x2 = max (x1 + min_dimension, x2);
4731 y2 = min (y1 - min_dimension, y2);
4733 y2 = max (y1 + min_dimension, y2);
4736 /* translate rect into item space and set */
4738 ArdourCanvas::Rect r (x1, y1, x2, y2);
4740 /* this drag is a _trackview_only == true drag, so the y1 and
4741 * y2 (computed using current_pointer_y() and grab_y()) will be
4742 * relative to the top of the trackview group). The
4743 * rubberband rect has the same parent/scroll offset as the
4744 * the trackview group, so we can use the "r" rect directly
4745 * to set the shape of the rubberband.
4748 _editor->rubberband_rect->set (r);
4749 _editor->rubberband_rect->show();
4750 _editor->rubberband_rect->raise_to_top();
4752 show_verbose_cursor_time (pf);
4754 do_select_things (event, true);
4759 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4763 framepos_t grab = grab_frame ();
4764 framepos_t lpf = last_pointer_frame ();
4766 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4767 grab = raw_grab_frame ();
4768 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4782 if (current_pointer_y() < grab_y()) {
4783 y1 = current_pointer_y();
4786 y2 = current_pointer_y();
4790 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4794 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4796 if (movement_occurred) {
4798 motion (event, false);
4799 do_select_things (event, false);
4805 bool do_deselect = true;
4806 MidiTimeAxisView* mtv;
4808 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4810 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4811 /* nothing selected */
4812 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4813 do_deselect = false;
4817 /* do not deselect if Primary or Tertiary (toggle-select or
4818 * extend-select are pressed.
4821 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4822 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4829 _editor->rubberband_rect->hide();
4833 RubberbandSelectDrag::aborted (bool)
4835 _editor->rubberband_rect->hide ();
4838 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4839 : RegionDrag (e, i, p, v)
4841 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4845 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4847 Drag::start_grab (event, cursor);
4849 _editor->get_selection().add (_primary);
4851 framepos_t where = _primary->region()->position();
4852 setup_snap_delta (where);
4854 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4858 TimeFXDrag::motion (GdkEvent* event, bool)
4860 RegionView* rv = _primary;
4861 StreamView* cv = rv->get_time_axis_view().view ();
4863 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4864 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4865 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4866 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4867 _editor->snap_to_with_modifier (pf, event);
4868 pf -= snap_delta (event->button.state);
4870 if (pf > rv->region()->position()) {
4871 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4874 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4878 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4880 /* this may have been a single click, no drag. We still want the dialog
4881 to show up in that case, so that the user can manually edit the
4882 parameters for the timestretch.
4885 float fraction = 1.0;
4887 if (movement_occurred) {
4889 motion (event, false);
4891 _primary->get_time_axis_view().hide_timestretch ();
4893 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4895 if (adjusted_frame_pos < _primary->region()->position()) {
4896 /* backwards drag of the left edge - not usable */
4900 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4902 fraction = (double) newlen / (double) _primary->region()->length();
4904 #ifndef USE_RUBBERBAND
4905 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4906 if (_primary->region()->data_type() == DataType::AUDIO) {
4907 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4912 if (!_editor->get_selection().regions.empty()) {
4913 /* primary will already be included in the selection, and edit
4914 group shared editing will propagate selection across
4915 equivalent regions, so just use the current region
4919 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4920 error << _("An error occurred while executing time stretch operation") << endmsg;
4926 TimeFXDrag::aborted (bool)
4928 _primary->get_time_axis_view().hide_timestretch ();
4931 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4934 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4938 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4940 Drag::start_grab (event);
4944 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4946 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4950 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4952 if (movement_occurred && _editor->session()) {
4953 /* make sure we stop */
4954 _editor->session()->request_transport_speed (0.0);
4959 ScrubDrag::aborted (bool)
4964 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4968 , _time_selection_at_start (!_editor->get_selection().time.empty())
4970 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4972 if (_time_selection_at_start) {
4973 start_at_start = _editor->get_selection().time.start();
4974 end_at_start = _editor->get_selection().time.end_frame();
4979 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4981 if (_editor->session() == 0) {
4985 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4987 switch (_operation) {
4988 case CreateSelection:
4989 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4994 cursor = _editor->cursors()->selector;
4995 Drag::start_grab (event, cursor);
4998 case SelectionStartTrim:
4999 if (_editor->clicked_axisview) {
5000 _editor->clicked_axisview->order_selection_trims (_item, true);
5002 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5005 case SelectionEndTrim:
5006 if (_editor->clicked_axisview) {
5007 _editor->clicked_axisview->order_selection_trims (_item, false);
5009 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5013 Drag::start_grab (event, cursor);
5016 case SelectionExtend:
5017 Drag::start_grab (event, cursor);
5021 if (_operation == SelectionMove) {
5022 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5024 show_verbose_cursor_time (adjusted_current_frame (event));
5029 SelectionDrag::setup_pointer_frame_offset ()
5031 switch (_operation) {
5032 case CreateSelection:
5033 _pointer_frame_offset = 0;
5036 case SelectionStartTrim:
5038 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5041 case SelectionEndTrim:
5042 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5045 case SelectionExtend:
5051 SelectionDrag::motion (GdkEvent* event, bool first_move)
5053 framepos_t start = 0;
5055 framecnt_t length = 0;
5056 framecnt_t distance = 0;
5058 framepos_t const pending_position = adjusted_current_frame (event);
5060 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5064 switch (_operation) {
5065 case CreateSelection:
5067 framepos_t grab = grab_frame ();
5070 grab = adjusted_current_frame (event, false);
5071 if (grab < pending_position) {
5072 _editor->snap_to (grab, RoundDownMaybe);
5074 _editor->snap_to (grab, RoundUpMaybe);
5078 if (pending_position < grab) {
5079 start = pending_position;
5082 end = pending_position;
5086 /* first drag: Either add to the selection
5087 or create a new selection
5094 /* adding to the selection */
5095 _editor->set_selected_track_as_side_effect (Selection::Add);
5096 _editor->clicked_selection = _editor->selection->add (start, end);
5103 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5104 _editor->set_selected_track_as_side_effect (Selection::Set);
5107 _editor->clicked_selection = _editor->selection->set (start, end);
5111 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5112 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5113 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5115 _editor->selection->add (atest);
5119 /* select all tracks within the rectangle that we've marked out so far */
5120 TrackViewList new_selection;
5121 TrackViewList& all_tracks (_editor->track_views);
5123 ArdourCanvas::Coord const top = grab_y();
5124 ArdourCanvas::Coord const bottom = current_pointer_y();
5126 if (top >= 0 && bottom >= 0) {
5128 //first, find the tracks that are covered in the y range selection
5129 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5130 if ((*i)->covered_by_y_range (top, bottom)) {
5131 new_selection.push_back (*i);
5135 //now find any tracks that are GROUPED with the tracks we selected
5136 TrackViewList grouped_add = new_selection;
5137 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5138 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5139 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5140 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5141 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5142 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5143 grouped_add.push_back (*j);
5148 //now compare our list with the current selection, and add or remove as necessary
5149 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5150 TrackViewList tracks_to_add;
5151 TrackViewList tracks_to_remove;
5152 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5153 if ( !_editor->selection->tracks.contains ( *i ) )
5154 tracks_to_add.push_back ( *i );
5155 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5156 if ( !grouped_add.contains ( *i ) )
5157 tracks_to_remove.push_back ( *i );
5158 _editor->selection->add(tracks_to_add);
5159 _editor->selection->remove(tracks_to_remove);
5165 case SelectionStartTrim:
5167 end = _editor->selection->time[_editor->clicked_selection].end;
5169 if (pending_position > end) {
5172 start = pending_position;
5176 case SelectionEndTrim:
5178 start = _editor->selection->time[_editor->clicked_selection].start;
5180 if (pending_position < start) {
5183 end = pending_position;
5190 start = _editor->selection->time[_editor->clicked_selection].start;
5191 end = _editor->selection->time[_editor->clicked_selection].end;
5193 length = end - start;
5194 distance = pending_position - start;
5195 start = pending_position;
5196 _editor->snap_to (start);
5198 end = start + length;
5202 case SelectionExtend:
5207 switch (_operation) {
5209 if (_time_selection_at_start) {
5210 _editor->selection->move_time (distance);
5214 _editor->selection->replace (_editor->clicked_selection, start, end);
5218 if (_operation == SelectionMove) {
5219 show_verbose_cursor_time(start);
5221 show_verbose_cursor_time(pending_position);
5226 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5228 Session* s = _editor->session();
5230 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5231 if (movement_occurred) {
5232 motion (event, false);
5233 /* XXX this is not object-oriented programming at all. ick */
5234 if (_editor->selection->time.consolidate()) {
5235 _editor->selection->TimeChanged ();
5238 /* XXX what if its a music time selection? */
5240 if (s->get_play_range() && s->transport_rolling()) {
5241 s->request_play_range (&_editor->selection->time, true);
5242 } else if (!s->config.get_external_sync()) {
5243 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5244 if (_operation == SelectionEndTrim)
5245 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5247 s->request_locate (_editor->get_selection().time.start());
5251 if (_editor->get_selection().time.length() != 0) {
5252 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5254 s->clear_range_selection ();
5259 /* just a click, no pointer movement.
5262 if (_operation == SelectionExtend) {
5263 if (_time_selection_at_start) {
5264 framepos_t pos = adjusted_current_frame (event, false);
5265 framepos_t start = min (pos, start_at_start);
5266 framepos_t end = max (pos, end_at_start);
5267 _editor->selection->set (start, end);
5270 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5271 if (_editor->clicked_selection) {
5272 _editor->selection->remove (_editor->clicked_selection);
5275 if (!_editor->clicked_selection) {
5276 _editor->selection->clear_time();
5281 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5282 _editor->selection->set (_editor->clicked_axisview);
5285 if (s && s->get_play_range () && s->transport_rolling()) {
5286 s->request_stop (false, false);
5291 _editor->stop_canvas_autoscroll ();
5292 _editor->clicked_selection = 0;
5293 _editor->commit_reversible_selection_op ();
5297 SelectionDrag::aborted (bool)
5302 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5303 : Drag (e, i, false),
5307 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5309 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5310 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5311 physical_screen_height (_editor->current_toplevel()->get_window())));
5312 _drag_rect->hide ();
5314 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5315 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5318 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5320 /* normal canvas items will be cleaned up when their parent group is deleted. But
5321 this item is created as the child of a long-lived parent group, and so we
5322 need to explicitly delete it.
5328 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5330 if (_editor->session() == 0) {
5334 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5336 if (!_editor->temp_location) {
5337 _editor->temp_location = new Location (*_editor->session());
5340 switch (_operation) {
5341 case CreateSkipMarker:
5342 case CreateRangeMarker:
5343 case CreateTransportMarker:
5344 case CreateCDMarker:
5346 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5351 cursor = _editor->cursors()->selector;
5355 Drag::start_grab (event, cursor);
5357 show_verbose_cursor_time (adjusted_current_frame (event));
5361 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5363 framepos_t start = 0;
5365 ArdourCanvas::Rectangle *crect;
5367 switch (_operation) {
5368 case CreateSkipMarker:
5369 crect = _editor->range_bar_drag_rect;
5371 case CreateRangeMarker:
5372 crect = _editor->range_bar_drag_rect;
5374 case CreateTransportMarker:
5375 crect = _editor->transport_bar_drag_rect;
5377 case CreateCDMarker:
5378 crect = _editor->cd_marker_bar_drag_rect;
5381 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5386 framepos_t const pf = adjusted_current_frame (event);
5388 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5389 framepos_t grab = grab_frame ();
5390 _editor->snap_to (grab);
5392 if (pf < grab_frame()) {
5400 /* first drag: Either add to the selection
5401 or create a new selection.
5406 _editor->temp_location->set (start, end);
5410 update_item (_editor->temp_location);
5412 //_drag_rect->raise_to_top();
5418 _editor->temp_location->set (start, end);
5420 double x1 = _editor->sample_to_pixel (start);
5421 double x2 = _editor->sample_to_pixel (end);
5425 update_item (_editor->temp_location);
5428 show_verbose_cursor_time (pf);
5433 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5435 Location * newloc = 0;
5439 if (movement_occurred) {
5440 motion (event, false);
5443 switch (_operation) {
5444 case CreateSkipMarker:
5445 case CreateRangeMarker:
5446 case CreateCDMarker:
5448 XMLNode &before = _editor->session()->locations()->get_state();
5449 if (_operation == CreateSkipMarker) {
5450 _editor->begin_reversible_command (_("new skip marker"));
5451 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5452 flags = Location::IsRangeMarker | Location::IsSkip;
5453 _editor->range_bar_drag_rect->hide();
5454 } else if (_operation == CreateCDMarker) {
5455 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5456 _editor->begin_reversible_command (_("new CD marker"));
5457 flags = Location::IsRangeMarker | Location::IsCDMarker;
5458 _editor->cd_marker_bar_drag_rect->hide();
5460 _editor->begin_reversible_command (_("new skip marker"));
5461 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5462 flags = Location::IsRangeMarker;
5463 _editor->range_bar_drag_rect->hide();
5465 newloc = new Location (
5466 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5469 _editor->session()->locations()->add (newloc, true);
5470 XMLNode &after = _editor->session()->locations()->get_state();
5471 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5472 _editor->commit_reversible_command ();
5476 case CreateTransportMarker:
5477 // popup menu to pick loop or punch
5478 _editor->new_transport_marker_context_menu (&event->button, _item);
5484 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5486 if (_operation == CreateTransportMarker) {
5488 /* didn't drag, so just locate */
5490 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5492 } else if (_operation == CreateCDMarker) {
5494 /* didn't drag, but mark is already created so do
5497 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5502 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5504 if (end == max_framepos) {
5505 end = _editor->session()->current_end_frame ();
5508 if (start == max_framepos) {
5509 start = _editor->session()->current_start_frame ();
5512 switch (_editor->mouse_mode) {
5514 /* find the two markers on either side and then make the selection from it */
5515 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5519 /* find the two markers on either side of the click and make the range out of it */
5520 _editor->selection->set (start, end);
5529 _editor->stop_canvas_autoscroll ();
5533 RangeMarkerBarDrag::aborted (bool movement_occurred)
5535 if (movement_occurred) {
5536 _drag_rect->hide ();
5541 RangeMarkerBarDrag::update_item (Location* location)
5543 double const x1 = _editor->sample_to_pixel (location->start());
5544 double const x2 = _editor->sample_to_pixel (location->end());
5546 _drag_rect->set_x0 (x1);
5547 _drag_rect->set_x1 (x2);
5550 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5552 , _cumulative_dx (0)
5553 , _cumulative_dy (0)
5554 , _was_selected (false)
5556 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5558 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5560 _region = &_primary->region_view ();
5561 _note_height = _region->midi_stream_view()->note_height ();
5565 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5567 Drag::start_grab (event);
5568 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5570 if (!(_was_selected = _primary->selected())) {
5572 /* tertiary-click means extend selection - we'll do that on button release,
5573 so don't add it here, because otherwise we make it hard to figure
5574 out the "extend-to" range.
5577 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5580 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5583 _region->note_selected (_primary, true);
5585 _editor->get_selection().clear_points();
5586 _region->unique_select (_primary);
5592 /** @return Current total drag x change in frames */
5594 NoteDrag::total_dx (const guint state) const
5596 if (_x_constrained) {
5599 TempoMap& map (_editor->session()->tempo_map());
5602 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5604 /* primary note time */
5605 double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
5606 frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5608 /* new time of the primary note in session frames */
5609 frameoffset_t st = n + dx + snap_delta (state);
5611 framepos_t const rp = _region->region()->position ();
5613 /* prevent the note being dragged earlier than the region's position */
5616 /* possibly snap and return corresponding delta */
5620 if (ArdourKeyboard::indicates_snap (state)) {
5621 if (_editor->snap_mode () != SnapOff) {
5625 if (_editor->snap_mode () == SnapOff) {
5627 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5628 if (ArdourKeyboard::indicates_snap_delta (state)) {
5636 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5637 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5639 ret = st - n - snap_delta (state);
5644 /** @return Current total drag y change in note number */
5646 NoteDrag::total_dy () const
5648 if (_y_constrained) {
5652 MidiStreamView* msv = _region->midi_stream_view ();
5653 double const y = _region->midi_view()->y_position ();
5654 /* new current note */
5655 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5657 n = max (msv->lowest_note(), n);
5658 n = min (msv->highest_note(), n);
5659 /* and work out delta */
5660 return n - msv->y_to_note (grab_y() - y);
5664 NoteDrag::motion (GdkEvent * event, bool)
5666 /* Total change in x and y since the start of the drag */
5667 frameoffset_t const dx = total_dx (event->button.state);
5668 int8_t const dy = total_dy ();
5670 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5671 double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5672 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5675 _cumulative_dx += tdx;
5676 _cumulative_dy += tdy;
5678 int8_t note_delta = total_dy();
5681 _region->move_selection (tdx, tdy, note_delta);
5683 /* the new note value may be the same as the old one, but we
5684 * don't know what that means because the selection may have
5685 * involved more than one note and we might be doing something
5686 * odd with them. so show the note value anyway, always.
5689 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5691 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5697 NoteDrag::finished (GdkEvent* ev, bool moved)
5700 /* no motion - select note */
5702 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5703 _editor->current_mouse_mode() == Editing::MouseDraw) {
5705 bool changed = false;
5707 if (_was_selected) {
5708 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5710 _region->note_deselected (_primary);
5713 _editor->get_selection().clear_points();
5714 _region->unique_select (_primary);
5718 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5719 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5721 if (!extend && !add && _region->selection_size() > 1) {
5722 _editor->get_selection().clear_points();
5723 _region->unique_select (_primary);
5725 } else if (extend) {
5726 _region->note_selected (_primary, true, true);
5729 /* it was added during button press */
5736 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5737 _editor->commit_reversible_selection_op();
5741 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5746 NoteDrag::aborted (bool)
5751 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5752 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5753 : Drag (editor, atv->base_item ())
5755 , _y_origin (atv->y_position())
5756 , _nothing_to_drag (false)
5758 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5759 setup (atv->lines ());
5762 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5763 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5764 : Drag (editor, rv->get_canvas_group ())
5766 , _y_origin (rv->get_time_axis_view().y_position())
5767 , _nothing_to_drag (false)
5770 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5772 list<boost::shared_ptr<AutomationLine> > lines;
5774 AudioRegionView* audio_view;
5775 AutomationRegionView* automation_view;
5776 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5777 lines.push_back (audio_view->get_gain_line ());
5778 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5779 lines.push_back (automation_view->line ());
5782 error << _("Automation range drag created for invalid region type") << endmsg;
5788 /** @param lines AutomationLines to drag.
5789 * @param offset Offset from the session start to the points in the AutomationLines.
5792 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5794 /* find the lines that overlap the ranges being dragged */
5795 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5796 while (i != lines.end ()) {
5797 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5800 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5802 /* check this range against all the AudioRanges that we are using */
5803 list<AudioRange>::const_iterator k = _ranges.begin ();
5804 while (k != _ranges.end()) {
5805 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5811 /* add it to our list if it overlaps at all */
5812 if (k != _ranges.end()) {
5817 _lines.push_back (n);
5823 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5827 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5829 return 1.0 - ((global_y - _y_origin) / line->height());
5833 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5835 const double v = list->eval(x);
5836 return _integral ? rint(v) : v;
5840 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5842 Drag::start_grab (event, cursor);
5844 /* Get line states before we start changing things */
5845 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5846 i->state = &i->line->get_state ();
5847 i->original_fraction = y_fraction (i->line, current_pointer_y());
5850 if (_ranges.empty()) {
5852 /* No selected time ranges: drag all points */
5853 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5854 uint32_t const N = i->line->npoints ();
5855 for (uint32_t j = 0; j < N; ++j) {
5856 i->points.push_back (i->line->nth (j));
5862 if (_nothing_to_drag) {
5868 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5870 if (_nothing_to_drag && !first_move) {
5875 _editor->begin_reversible_command (_("automation range move"));
5877 if (!_ranges.empty()) {
5879 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5881 framecnt_t const half = (i->start + i->end) / 2;
5883 /* find the line that this audio range starts in */
5884 list<Line>::iterator j = _lines.begin();
5885 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5889 if (j != _lines.end()) {
5890 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5892 /* j is the line that this audio range starts in; fade into it;
5893 64 samples length plucked out of thin air.
5896 framepos_t a = i->start + 64;
5901 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5902 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5904 XMLNode &before = the_list->get_state();
5905 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5906 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5908 if (add_p || add_q) {
5909 _editor->session()->add_command (
5910 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5914 /* same thing for the end */
5917 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5921 if (j != _lines.end()) {
5922 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5924 /* j is the line that this audio range starts in; fade out of it;
5925 64 samples length plucked out of thin air.
5928 framepos_t b = i->end - 64;
5933 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5934 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5936 XMLNode &before = the_list->get_state();
5937 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5938 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5940 if (add_p || add_q) {
5941 _editor->session()->add_command (
5942 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5947 _nothing_to_drag = true;
5949 /* Find all the points that should be dragged and put them in the relevant
5950 points lists in the Line structs.
5953 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5955 uint32_t const N = i->line->npoints ();
5956 for (uint32_t j = 0; j < N; ++j) {
5958 /* here's a control point on this line */
5959 ControlPoint* p = i->line->nth (j);
5960 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5962 /* see if it's inside a range */
5963 list<AudioRange>::const_iterator k = _ranges.begin ();
5964 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5968 if (k != _ranges.end()) {
5969 /* dragging this point */
5970 _nothing_to_drag = false;
5971 i->points.push_back (p);
5977 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5978 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5982 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5983 float const f = y_fraction (l->line, current_pointer_y());
5984 /* we are ignoring x position for this drag, so we can just pass in anything */
5985 pair<double, float> result;
5987 result = l->line->drag_motion (0, f, true, false, ignored);
5988 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5993 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5995 if (_nothing_to_drag || !motion_occurred) {
5999 motion (event, false);
6000 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6001 i->line->end_drag (false, 0);
6004 _editor->commit_reversible_command ();
6008 AutomationRangeDrag::aborted (bool)
6010 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6015 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6017 , initial_time_axis_view (itav)
6019 /* note that time_axis_view may be null if the regionview was created
6020 * as part of a copy operation.
6022 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6023 layer = v->region()->layer ();
6024 initial_y = v->get_canvas_group()->position().y;
6025 initial_playlist = v->region()->playlist ();
6026 initial_position = v->region()->position ();
6027 initial_end = v->region()->position () + v->region()->length ();
6030 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6031 : Drag (e, i->canvas_item ())
6034 , _cumulative_dx (0)
6036 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6037 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6042 PatchChangeDrag::motion (GdkEvent* ev, bool)
6044 framepos_t f = adjusted_current_frame (ev);
6045 boost::shared_ptr<Region> r = _region_view->region ();
6046 f = max (f, r->position ());
6047 f = min (f, r->last_frame ());
6049 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6050 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6051 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6052 _cumulative_dx = dxu;
6056 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6058 if (!movement_occurred) {
6059 if (was_double_click()) {
6060 _region_view->edit_patch_change (_patch_change);
6065 boost::shared_ptr<Region> r (_region_view->region ());
6066 framepos_t f = adjusted_current_frame (ev);
6067 f = max (f, r->position ());
6068 f = min (f, r->last_frame ());
6070 _region_view->move_patch_change (
6072 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6077 PatchChangeDrag::aborted (bool)
6079 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6083 PatchChangeDrag::setup_pointer_frame_offset ()
6085 boost::shared_ptr<Region> region = _region_view->region ();
6086 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6089 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6090 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6097 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6099 _region_view->update_drag_selection (
6101 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6105 MidiRubberbandSelectDrag::deselect_things ()
6110 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6111 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6114 _vertical_only = true;
6118 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6120 double const y = _region_view->midi_view()->y_position ();
6122 y1 = max (0.0, y1 - y);
6123 y2 = max (0.0, y2 - y);
6125 _region_view->update_vertical_drag_selection (
6128 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6133 MidiVerticalSelectDrag::deselect_things ()
6138 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6139 : RubberbandSelectDrag (e, i)
6145 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6147 if (drag_in_progress) {
6148 /* We just want to select things at the end of the drag, not during it */
6152 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6154 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6156 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6158 _editor->commit_reversible_selection_op ();
6162 EditorRubberbandSelectDrag::deselect_things ()
6164 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6166 _editor->selection->clear_tracks();
6167 _editor->selection->clear_regions();
6168 _editor->selection->clear_points ();
6169 _editor->selection->clear_lines ();
6170 _editor->selection->clear_midi_notes ();
6172 _editor->commit_reversible_selection_op();
6175 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6180 _note[0] = _note[1] = 0;
6183 NoteCreateDrag::~NoteCreateDrag ()
6189 NoteCreateDrag::grid_frames (framepos_t t) const
6192 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6194 grid_beats = Evoral::Beats(1);
6197 return _region_view->region_beats_to_region_frames (grid_beats);
6201 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6203 Drag::start_grab (event, cursor);
6205 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6206 TempoMap& map (_editor->session()->tempo_map());
6208 const framepos_t pf = _drags->current_pointer_frame ();
6209 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6211 double eqaf = map.exact_qn_at_frame (pf, divisions);
6213 if (divisions != 0) {
6214 bool success = false;
6215 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf);
6217 grid_beats = Evoral::Beats(1);
6220 const double qaf = map.quarter_note_at_frame (pf);
6222 /* Hack so that we always snap to the note that we are over, instead of snapping
6223 to the next one if we're more than halfway through the one we're over.
6226 const double rem = eqaf - qaf;
6228 eqaf -= grid_beats.to_double();
6232 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6233 _note[1] = _note[0];
6235 MidiStreamView* sv = _region_view->midi_stream_view ();
6236 double const x = _editor->sample_to_pixel (_note[0]);
6237 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6239 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6240 _drag_rect->set_outline_all ();
6241 _drag_rect->set_outline_color (0xffffff99);
6242 _drag_rect->set_fill_color (0xffffff66);
6246 NoteCreateDrag::motion (GdkEvent* event, bool)
6248 TempoMap& map (_editor->session()->tempo_map());
6249 const framepos_t pf = _drags->current_pointer_frame ();
6250 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6251 double eqaf = map.exact_qn_at_frame (pf, divisions);
6253 if (divisions != 0) {
6254 bool success = false;
6255 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf);
6257 grid_beats = Evoral::Beats(1);
6260 const double qaf = map.quarter_note_at_frame (pf);
6261 /* Hack so that we always snap to the note that we are over, instead of snapping
6262 to the next one if we're more than halfway through the one we're over.
6265 const double rem = eqaf - qaf;
6267 eqaf -= grid_beats.to_double();
6270 eqaf += grid_beats.to_double();
6272 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6274 double const x0 = _editor->sample_to_pixel (_note[0]);
6275 double const x1 = _editor->sample_to_pixel (_note[1]);
6276 _drag_rect->set_x0 (std::min(x0, x1));
6277 _drag_rect->set_x1 (std::max(x0, x1));
6281 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6283 if (!had_movement) {
6287 framepos_t const start = min (_note[0], _note[1]);
6288 framepos_t const start_sess_rel = start + _region_view->region()->position();
6289 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6291 framecnt_t const g = grid_frames (start);
6292 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6294 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6298 TempoMap& map (_editor->session()->tempo_map());
6299 const double qn_length = map.quarter_note_at_frame (start_sess_rel + length) - map.quarter_note_at_frame (start_sess_rel);
6301 Evoral::Beats qn_length_beats = max (one_tick, Evoral::Beats (qn_length));
6302 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6306 NoteCreateDrag::y_to_region (double y) const
6309 _region_view->get_canvas_group()->canvas_to_item (x, y);
6314 NoteCreateDrag::aborted (bool)
6319 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6324 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6328 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6330 Drag::start_grab (event, cursor);
6334 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6340 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6343 distance = _drags->current_pointer_x() - grab_x();
6344 len = ar->fade_in()->back()->when;
6346 distance = grab_x() - _drags->current_pointer_x();
6347 len = ar->fade_out()->back()->when;
6350 /* how long should it be ? */
6352 new_length = len + _editor->pixel_to_sample (distance);
6354 /* now check with the region that this is legal */
6356 new_length = ar->verify_xfade_bounds (new_length, start);
6359 arv->reset_fade_in_shape_width (ar, new_length);
6361 arv->reset_fade_out_shape_width (ar, new_length);
6366 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6372 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6375 distance = _drags->current_pointer_x() - grab_x();
6376 len = ar->fade_in()->back()->when;
6378 distance = grab_x() - _drags->current_pointer_x();
6379 len = ar->fade_out()->back()->when;
6382 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6384 _editor->begin_reversible_command ("xfade trim");
6385 ar->playlist()->clear_owned_changes ();
6388 ar->set_fade_in_length (new_length);
6390 ar->set_fade_out_length (new_length);
6393 /* Adjusting the xfade may affect other regions in the playlist, so we need
6394 to get undo Commands from the whole playlist rather than just the
6398 vector<Command*> cmds;
6399 ar->playlist()->rdiff (cmds);
6400 _editor->session()->add_commands (cmds);
6401 _editor->commit_reversible_command ();
6406 CrossfadeEdgeDrag::aborted (bool)
6409 // arv->redraw_start_xfade ();
6411 // arv->redraw_end_xfade ();
6415 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6416 : Drag (e, item, true)
6417 , line (new EditorCursor (*e))
6419 line->set_position (pos);
6423 RegionCutDrag::~RegionCutDrag ()
6429 RegionCutDrag::motion (GdkEvent*, bool)
6431 framepos_t where = _drags->current_pointer_frame();
6432 _editor->snap_to (where);
6434 line->set_position (where);
6438 RegionCutDrag::finished (GdkEvent* event, bool)
6440 _editor->get_track_canvas()->canvas()->re_enter();
6442 framepos_t pos = _drags->current_pointer_frame();
6446 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6452 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state));
6456 RegionCutDrag::aborted (bool)
6460 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6461 : Drag (e, item, true)
6466 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6468 Drag::start_grab (event, c);
6470 framepos_t where = _editor->canvas_event_sample(event);
6472 _editor->_dragging_playhead = true;
6474 _editor->playhead_cursor->set_position (where);
6478 RulerZoomDrag::motion (GdkEvent* event, bool)
6480 framepos_t where = _editor->canvas_event_sample(event);
6482 _editor->playhead_cursor->set_position (where);
6484 const double movement_limit = 20.0;
6485 const double scale = 1.08;
6486 const double y_delta = last_pointer_y() - current_pointer_y();
6488 if (y_delta > 0 && y_delta < movement_limit) {
6489 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6490 } else if (y_delta < 0 && y_delta > -movement_limit) {
6491 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6496 RulerZoomDrag::finished (GdkEvent*, bool)
6498 _editor->_dragging_playhead = false;
6500 Session* s = _editor->session ();
6502 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6503 _editor->_pending_locate_request = true;
6509 RulerZoomDrag::aborted (bool)