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);
2926 switch (_operation) {
2928 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2929 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2930 , _editor->get_grid_music_divisions (event->button.state));
2932 if (changed && _preserve_fade_anchor) {
2933 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2935 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2936 framecnt_t len = ar->fade_in()->back()->when;
2937 framecnt_t diff = ar->first_frame() - i->initial_position;
2938 framepos_t new_length = len - diff;
2939 i->anchored_fade_length = min (ar->length(), new_length);
2940 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2941 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2948 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2949 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, _editor->get_grid_music_divisions (event->button.state));
2950 if (changed && _preserve_fade_anchor) {
2951 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2953 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2954 framecnt_t len = ar->fade_out()->back()->when;
2955 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2956 framepos_t new_length = len + diff;
2957 i->anchored_fade_length = min (ar->length(), new_length);
2958 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2959 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2967 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2969 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2970 i->view->move_contents (frame_delta);
2976 switch (_operation) {
2978 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2981 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2984 // show_verbose_cursor_time (frame_delta);
2990 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2992 if (movement_occurred) {
2993 motion (event, false);
2995 if (_operation == StartTrim) {
2996 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2998 /* This must happen before the region's StatefulDiffCommand is created, as it may
2999 `correct' (ahem) the region's _start from being negative to being zero. It
3000 needs to be zero in the undo record.
3002 i->view->trim_front_ending ();
3004 if (_preserve_fade_anchor) {
3005 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3007 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3008 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3009 ar->set_fade_in_length(i->anchored_fade_length);
3010 ar->set_fade_in_active(true);
3013 if (_jump_position_when_done) {
3014 i->view->region()->set_position (i->initial_position);
3017 } else if (_operation == EndTrim) {
3018 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3019 if (_preserve_fade_anchor) {
3020 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3022 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3023 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3024 ar->set_fade_out_length(i->anchored_fade_length);
3025 ar->set_fade_out_active(true);
3028 if (_jump_position_when_done) {
3029 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3034 if (!_views.empty()) {
3035 if (_operation == StartTrim) {
3036 _editor->maybe_locate_with_edit_preroll(
3037 _views.begin()->view->region()->position());
3039 if (_operation == EndTrim) {
3040 _editor->maybe_locate_with_edit_preroll(
3041 _views.begin()->view->region()->position() +
3042 _views.begin()->view->region()->length());
3046 if (!_editor->selection->selected (_primary)) {
3047 _primary->thaw_after_trim ();
3050 set<boost::shared_ptr<Playlist> > diffed_playlists;
3052 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3053 i->view->thaw_after_trim ();
3054 i->view->enable_display (true);
3056 /* Trimming one region may affect others on the playlist, so we need
3057 to get undo Commands from the whole playlist rather than just the
3058 region. Use diffed_playlists to make sure we don't diff a given
3059 playlist more than once.
3061 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3062 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3063 vector<Command*> cmds;
3065 _editor->session()->add_commands (cmds);
3066 diffed_playlists.insert (p);
3071 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3075 _editor->motion_frozen_playlists.clear ();
3076 _editor->commit_reversible_command();
3079 /* no mouse movement */
3080 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3081 _editor->point_trim (event, adjusted_current_frame (event));
3085 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3086 i->view->region()->resume_property_changes ();
3091 TrimDrag::aborted (bool movement_occurred)
3093 /* Our motion method is changing model state, so use the Undo system
3094 to cancel. Perhaps not ideal, as this will leave an Undo point
3095 behind which may be slightly odd from the user's point of view.
3099 finished (&ev, true);
3101 if (movement_occurred) {
3102 _editor->session()->undo (1);
3105 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3106 i->view->region()->resume_property_changes ();
3111 TrimDrag::setup_pointer_frame_offset ()
3113 list<DraggingView>::iterator i = _views.begin ();
3114 while (i != _views.end() && i->view != _primary) {
3118 if (i == _views.end()) {
3122 switch (_operation) {
3124 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3127 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3134 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3137 , _old_snap_type (e->snap_type())
3138 , _old_snap_mode (e->snap_mode())
3141 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3142 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3144 _real_section = &_marker->meter();
3149 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3151 Drag::start_grab (event, cursor);
3152 show_verbose_cursor_time (adjusted_current_frame(event));
3156 MeterMarkerDrag::setup_pointer_frame_offset ()
3158 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3162 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3165 // create a dummy marker to catch events, then hide it.
3168 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3170 _marker = new MeterMarker (
3172 *_editor->meter_group,
3173 UIConfiguration::instance().color ("meter marker"),
3175 *new MeterSection (_marker->meter())
3178 /* use the new marker for the grab */
3179 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3182 TempoMap& map (_editor->session()->tempo_map());
3183 /* get current state */
3184 before_state = &map.get_state();
3187 _editor->begin_reversible_command (_("move meter mark"));
3189 _editor->begin_reversible_command (_("copy meter mark"));
3191 Timecode::BBT_Time bbt = _real_section->bbt();
3193 /* we can't add a meter where one currently exists */
3194 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3199 const double beat = map.beat_at_bbt (bbt);
3200 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3201 , beat, bbt, _real_section->position_lock_style());
3202 if (!_real_section) {
3208 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3209 if (_real_section->position_lock_style() != AudioTime) {
3210 _editor->set_snap_to (SnapToBar);
3211 _editor->set_snap_mode (SnapNormal);
3215 framepos_t pf = adjusted_current_frame (event);
3217 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3218 /* never snap to music for audio locked */
3219 pf = adjusted_current_frame (event, false);
3222 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3224 /* fake marker meeds to stay under the mouse, unlike the real one. */
3225 _marker->set_position (adjusted_current_frame (event, false));
3227 show_verbose_cursor_time (_real_section->frame());
3231 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3233 if (!movement_occurred) {
3234 if (was_double_click()) {
3235 _editor->edit_meter_marker (*_marker);
3240 /* reinstate old snap setting */
3241 _editor->set_snap_to (_old_snap_type);
3242 _editor->set_snap_mode (_old_snap_mode);
3244 TempoMap& map (_editor->session()->tempo_map());
3246 XMLNode &after = map.get_state();
3247 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3248 _editor->commit_reversible_command ();
3250 // delete the dummy marker we used for visual representation while moving.
3251 // a new visual marker will show up automatically.
3256 MeterMarkerDrag::aborted (bool moved)
3258 _marker->set_position (_marker->meter().frame ());
3260 /* reinstate old snap setting */
3261 _editor->set_snap_to (_old_snap_type);
3262 _editor->set_snap_mode (_old_snap_mode);
3264 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3265 // delete the dummy marker we used for visual representation while moving.
3266 // a new visual marker will show up automatically.
3271 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3277 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3279 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3280 _real_section = &_marker->tempo();
3281 _movable = _real_section->movable();
3282 _grab_bpm = _real_section->note_types_per_minute();
3287 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3289 Drag::start_grab (event, cursor);
3290 if (!_real_section->active()) {
3291 show_verbose_cursor_text (_("inactive"));
3293 show_verbose_cursor_time (adjusted_current_frame (event));
3298 TempoMarkerDrag::setup_pointer_frame_offset ()
3300 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3304 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3306 if (!_real_section->active()) {
3312 // mvc drag - create a dummy marker to catch events, hide it.
3315 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3317 TempoSection section (_marker->tempo());
3319 _marker = new TempoMarker (
3321 *_editor->tempo_group,
3322 UIConfiguration::instance().color ("tempo marker"),
3324 *new TempoSection (_marker->tempo())
3327 /* use the new marker for the grab */
3328 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3331 TempoMap& map (_editor->session()->tempo_map());
3332 /* get current state */
3333 before_state = &map.get_state();
3336 _editor->begin_reversible_command (_("move tempo mark"));
3339 const Tempo tempo (_marker->tempo());
3340 const framepos_t frame = adjusted_current_frame (event) + 1;
3341 const TempoSection::Type type = _real_section->type();
3343 _editor->begin_reversible_command (_("copy tempo mark"));
3345 if (_real_section->position_lock_style() == MusicTime) {
3346 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3347 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3349 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3352 if (!_real_section) {
3360 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3361 /* use vertical movement to alter tempo .. should be log */
3362 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3364 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3366 show_verbose_cursor_text (strs.str());
3368 } else if (_movable && !_real_section->locked_to_meter()) {
3371 if (_editor->snap_musical()) {
3372 /* we can't snap to a grid that we are about to move.
3373 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3375 pf = adjusted_current_frame (event, false);
3377 pf = adjusted_current_frame (event);
3380 TempoMap& map (_editor->session()->tempo_map());
3382 /* snap to beat is 1, snap to bar is -1 (sorry) */
3383 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3385 map.gui_move_tempo (_real_section, pf, sub_num);
3387 show_verbose_cursor_time (_real_section->frame());
3389 _marker->set_position (adjusted_current_frame (event, false));
3393 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3395 if (!_real_section->active()) {
3398 if (!movement_occurred) {
3399 if (was_double_click()) {
3400 _editor->edit_tempo_marker (*_marker);
3405 TempoMap& map (_editor->session()->tempo_map());
3407 XMLNode &after = map.get_state();
3408 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3409 _editor->commit_reversible_command ();
3411 // delete the dummy marker we used for visual representation while moving.
3412 // a new visual marker will show up automatically.
3417 TempoMarkerDrag::aborted (bool moved)
3419 _marker->set_position (_marker->tempo().frame());
3421 TempoMap& map (_editor->session()->tempo_map());
3422 map.set_state (*before_state, Stateful::current_state_version);
3423 // delete the dummy (hidden) marker we used for events while moving.
3428 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3434 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3439 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3441 Drag::start_grab (event, cursor);
3442 TempoMap& map (_editor->session()->tempo_map());
3443 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3446 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3447 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3448 show_verbose_cursor_text (sstr.str());
3449 finished (event, false);
3453 BBTRulerDrag::setup_pointer_frame_offset ()
3455 TempoMap& map (_editor->session()->tempo_map());
3456 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3457 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3460 if (divisions > 0) {
3461 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3463 /* while it makes some sense for the user to determine the division to 'grab',
3464 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3465 and the result over steep tempo curves. Use sixteenths.
3467 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3470 _grab_qn = map.quarter_note_at_beat (beat);
3472 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3477 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3479 TempoMap& map (_editor->session()->tempo_map());
3482 /* get current state */
3483 before_state = &map.get_state();
3484 _editor->begin_reversible_command (_("dilate tempo"));
3489 if (_editor->snap_musical()) {
3490 pf = adjusted_current_frame (event, false);
3492 pf = adjusted_current_frame (event);
3495 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3496 /* adjust previous tempo to match pointer frame */
3497 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3500 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3501 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3502 show_verbose_cursor_text (sstr.str());
3506 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3508 if (!movement_occurred) {
3512 TempoMap& map (_editor->session()->tempo_map());
3514 XMLNode &after = map.get_state();
3515 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3516 _editor->commit_reversible_command ();
3520 BBTRulerDrag::aborted (bool moved)
3523 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3528 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3529 : Drag (e, &c.track_canvas_item(), false)
3534 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3537 /** Do all the things we do when dragging the playhead to make it look as though
3538 * we have located, without actually doing the locate (because that would cause
3539 * the diskstream buffers to be refilled, which is too slow).
3542 CursorDrag::fake_locate (framepos_t t)
3544 if (_editor->session () == 0) {
3548 _editor->playhead_cursor->set_position (t);
3550 Session* s = _editor->session ();
3551 if (s->timecode_transmission_suspended ()) {
3552 framepos_t const f = _editor->playhead_cursor->current_frame ();
3553 /* This is asynchronous so it will be sent "now"
3555 s->send_mmc_locate (f);
3556 /* These are synchronous and will be sent during the next
3559 s->queue_full_time_code ();
3560 s->queue_song_position_pointer ();
3563 show_verbose_cursor_time (t);
3564 _editor->UpdateAllTransportClocks (t);
3568 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3570 Drag::start_grab (event, c);
3571 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3573 _grab_zoom = _editor->samples_per_pixel;
3575 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3577 _editor->snap_to_with_modifier (where, event);
3579 _editor->_dragging_playhead = true;
3581 Session* s = _editor->session ();
3583 /* grab the track canvas item as well */
3585 _cursor.track_canvas_item().grab();
3588 if (_was_rolling && _stop) {
3592 if (s->is_auditioning()) {
3593 s->cancel_audition ();
3597 if (AudioEngine::instance()->connected()) {
3599 /* do this only if we're the engine is connected
3600 * because otherwise this request will never be
3601 * serviced and we'll busy wait forever. likewise,
3602 * notice if we are disconnected while waiting for the
3603 * request to be serviced.
3606 s->request_suspend_timecode_transmission ();
3607 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3608 /* twiddle our thumbs */
3613 fake_locate (where - snap_delta (event->button.state));
3617 CursorDrag::motion (GdkEvent* event, bool)
3619 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3620 _editor->snap_to_with_modifier (where, event);
3621 if (where != last_pointer_frame()) {
3622 fake_locate (where - snap_delta (event->button.state));
3627 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3629 _editor->_dragging_playhead = false;
3631 _cursor.track_canvas_item().ungrab();
3633 if (!movement_occurred && _stop) {
3637 motion (event, false);
3639 Session* s = _editor->session ();
3641 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3642 _editor->_pending_locate_request = true;
3643 s->request_resume_timecode_transmission ();
3648 CursorDrag::aborted (bool)
3650 _cursor.track_canvas_item().ungrab();
3652 if (_editor->_dragging_playhead) {
3653 _editor->session()->request_resume_timecode_transmission ();
3654 _editor->_dragging_playhead = false;
3657 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3660 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3661 : RegionDrag (e, i, p, v)
3663 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3667 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3669 Drag::start_grab (event, cursor);
3671 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3672 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3673 setup_snap_delta (r->position ());
3675 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3679 FadeInDrag::setup_pointer_frame_offset ()
3681 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3682 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3683 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3687 FadeInDrag::motion (GdkEvent* event, bool)
3689 framecnt_t fade_length;
3691 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3692 _editor->snap_to_with_modifier (pos, event);
3693 pos -= snap_delta (event->button.state);
3695 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3697 if (pos < (region->position() + 64)) {
3698 fade_length = 64; // this should be a minimum defined somewhere
3699 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3700 fade_length = region->length() - region->fade_out()->back()->when - 1;
3702 fade_length = pos - region->position();
3705 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3707 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3713 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3716 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3720 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3722 if (!movement_occurred) {
3726 framecnt_t fade_length;
3727 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3728 _editor->snap_to_with_modifier (pos, event);
3729 pos -= snap_delta (event->button.state);
3731 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3733 if (pos < (region->position() + 64)) {
3734 fade_length = 64; // this should be a minimum defined somewhere
3735 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3736 fade_length = region->length() - region->fade_out()->back()->when - 1;
3738 fade_length = pos - region->position();
3741 bool in_command = false;
3743 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3745 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3751 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3752 XMLNode &before = alist->get_state();
3754 tmp->audio_region()->set_fade_in_length (fade_length);
3755 tmp->audio_region()->set_fade_in_active (true);
3758 _editor->begin_reversible_command (_("change fade in length"));
3761 XMLNode &after = alist->get_state();
3762 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3766 _editor->commit_reversible_command ();
3771 FadeInDrag::aborted (bool)
3773 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3774 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3780 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3784 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3785 : RegionDrag (e, i, p, v)
3787 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3791 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3793 Drag::start_grab (event, cursor);
3795 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3796 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3797 setup_snap_delta (r->last_frame ());
3799 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3803 FadeOutDrag::setup_pointer_frame_offset ()
3805 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3806 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3807 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3811 FadeOutDrag::motion (GdkEvent* event, bool)
3813 framecnt_t fade_length;
3815 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3816 _editor->snap_to_with_modifier (pos, event);
3817 pos -= snap_delta (event->button.state);
3819 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3821 if (pos > (region->last_frame() - 64)) {
3822 fade_length = 64; // this should really be a minimum fade defined somewhere
3823 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3824 fade_length = region->length() - region->fade_in()->back()->when - 1;
3826 fade_length = region->last_frame() - pos;
3829 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3831 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3837 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3840 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3844 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3846 if (!movement_occurred) {
3850 framecnt_t fade_length;
3852 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3853 _editor->snap_to_with_modifier (pos, event);
3854 pos -= snap_delta (event->button.state);
3856 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3858 if (pos > (region->last_frame() - 64)) {
3859 fade_length = 64; // this should really be a minimum fade defined somewhere
3860 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3861 fade_length = region->length() - region->fade_in()->back()->when - 1;
3863 fade_length = region->last_frame() - pos;
3866 bool in_command = false;
3868 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3870 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3876 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3877 XMLNode &before = alist->get_state();
3879 tmp->audio_region()->set_fade_out_length (fade_length);
3880 tmp->audio_region()->set_fade_out_active (true);
3883 _editor->begin_reversible_command (_("change fade out length"));
3886 XMLNode &after = alist->get_state();
3887 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3891 _editor->commit_reversible_command ();
3896 FadeOutDrag::aborted (bool)
3898 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3899 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3905 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3909 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3911 , _selection_changed (false)
3913 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3914 Gtk::Window* toplevel = _editor->current_toplevel();
3915 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3919 _points.push_back (ArdourCanvas::Duple (0, 0));
3921 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3924 MarkerDrag::~MarkerDrag ()
3926 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3931 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3933 location = new Location (*l);
3934 markers.push_back (m);
3939 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3941 Drag::start_grab (event, cursor);
3945 Location *location = _editor->find_location_from_marker (_marker, is_start);
3946 _editor->_dragging_edit_point = true;
3948 update_item (location);
3950 // _drag_line->show();
3951 // _line->raise_to_top();
3954 show_verbose_cursor_time (location->start());
3956 show_verbose_cursor_time (location->end());
3958 setup_snap_delta (is_start ? location->start() : location->end());
3960 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3963 case Selection::Toggle:
3964 /* we toggle on the button release */
3966 case Selection::Set:
3967 if (!_editor->selection->selected (_marker)) {
3968 _editor->selection->set (_marker);
3969 _selection_changed = true;
3972 case Selection::Extend:
3974 Locations::LocationList ll;
3975 list<ArdourMarker*> to_add;
3977 _editor->selection->markers.range (s, e);
3978 s = min (_marker->position(), s);
3979 e = max (_marker->position(), e);
3982 if (e < max_framepos) {
3985 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3986 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3987 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3990 to_add.push_back (lm->start);
3993 to_add.push_back (lm->end);
3997 if (!to_add.empty()) {
3998 _editor->selection->add (to_add);
3999 _selection_changed = true;
4003 case Selection::Add:
4004 _editor->selection->add (_marker);
4005 _selection_changed = true;
4010 /* Set up copies for us to manipulate during the drag
4013 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4015 Location* l = _editor->find_location_from_marker (*i, is_start);
4022 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4024 /* range: check that the other end of the range isn't
4027 CopiedLocationInfo::iterator x;
4028 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4029 if (*(*x).location == *l) {
4033 if (x == _copied_locations.end()) {
4034 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4036 (*x).markers.push_back (*i);
4037 (*x).move_both = true;
4045 MarkerDrag::setup_pointer_frame_offset ()
4048 Location *location = _editor->find_location_from_marker (_marker, is_start);
4049 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4053 MarkerDrag::motion (GdkEvent* event, bool)
4055 framecnt_t f_delta = 0;
4057 bool move_both = false;
4058 Location *real_location;
4059 Location *copy_location = 0;
4060 framecnt_t const sd = snap_delta (event->button.state);
4062 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4063 framepos_t next = newframe;
4065 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4069 CopiedLocationInfo::iterator x;
4071 /* find the marker we're dragging, and compute the delta */
4073 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4075 copy_location = (*x).location;
4077 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4079 /* this marker is represented by this
4080 * CopiedLocationMarkerInfo
4083 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4088 if (real_location->is_mark()) {
4089 f_delta = newframe - copy_location->start();
4093 switch (_marker->type()) {
4094 case ArdourMarker::SessionStart:
4095 case ArdourMarker::RangeStart:
4096 case ArdourMarker::LoopStart:
4097 case ArdourMarker::PunchIn:
4098 f_delta = newframe - copy_location->start();
4101 case ArdourMarker::SessionEnd:
4102 case ArdourMarker::RangeEnd:
4103 case ArdourMarker::LoopEnd:
4104 case ArdourMarker::PunchOut:
4105 f_delta = newframe - copy_location->end();
4108 /* what kind of marker is this ? */
4117 if (x == _copied_locations.end()) {
4118 /* hmm, impossible - we didn't find the dragged marker */
4122 /* now move them all */
4124 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4126 copy_location = x->location;
4128 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4132 if (real_location->locked()) {
4136 if (copy_location->is_mark()) {
4140 copy_location->set_start (copy_location->start() + f_delta);
4144 framepos_t new_start = copy_location->start() + f_delta;
4145 framepos_t new_end = copy_location->end() + f_delta;
4147 if (is_start) { // start-of-range marker
4149 if (move_both || (*x).move_both) {
4150 copy_location->set_start (new_start);
4151 copy_location->set_end (new_end);
4152 } else if (new_start < copy_location->end()) {
4153 copy_location->set_start (new_start);
4154 } else if (newframe > 0) {
4155 //_editor->snap_to (next, RoundUpAlways, true);
4156 copy_location->set_end (next);
4157 copy_location->set_start (newframe);
4160 } else { // end marker
4162 if (move_both || (*x).move_both) {
4163 copy_location->set_end (new_end);
4164 copy_location->set_start (new_start);
4165 } else if (new_end > copy_location->start()) {
4166 copy_location->set_end (new_end);
4167 } else if (newframe > 0) {
4168 //_editor->snap_to (next, RoundDownAlways, true);
4169 copy_location->set_start (next);
4170 copy_location->set_end (newframe);
4175 update_item (copy_location);
4177 /* now lookup the actual GUI items used to display this
4178 * location and move them to wherever the copy of the location
4179 * is now. This means that the logic in ARDOUR::Location is
4180 * still enforced, even though we are not (yet) modifying
4181 * the real Location itself.
4184 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4187 lm->set_position (copy_location->start(), copy_location->end());
4192 assert (!_copied_locations.empty());
4194 show_verbose_cursor_time (newframe);
4198 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4200 if (!movement_occurred) {
4202 if (was_double_click()) {
4203 _editor->rename_marker (_marker);
4207 /* just a click, do nothing but finish
4208 off the selection process
4211 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4213 case Selection::Set:
4214 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4215 _editor->selection->set (_marker);
4216 _selection_changed = true;
4220 case Selection::Toggle:
4221 /* we toggle on the button release, click only */
4222 _editor->selection->toggle (_marker);
4223 _selection_changed = true;
4227 case Selection::Extend:
4228 case Selection::Add:
4232 if (_selection_changed) {
4233 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4234 _editor->commit_reversible_selection_op();
4240 _editor->_dragging_edit_point = false;
4242 XMLNode &before = _editor->session()->locations()->get_state();
4243 bool in_command = false;
4245 MarkerSelection::iterator i;
4246 CopiedLocationInfo::iterator x;
4249 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4250 x != _copied_locations.end() && i != _editor->selection->markers.end();
4253 Location * location = _editor->find_location_from_marker (*i, is_start);
4257 if (location->locked()) {
4261 _editor->begin_reversible_command ( _("move marker") );
4264 if (location->is_mark()) {
4265 location->set_start (((*x).location)->start());
4267 location->set (((*x).location)->start(), ((*x).location)->end());
4270 if (location->is_session_range()) {
4271 _editor->session()->set_end_is_free (false);
4277 XMLNode &after = _editor->session()->locations()->get_state();
4278 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4279 _editor->commit_reversible_command ();
4284 MarkerDrag::aborted (bool movement_occurred)
4286 if (!movement_occurred) {
4290 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4292 /* move all markers to their original location */
4295 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4298 Location * location = _editor->find_location_from_marker (*m, is_start);
4301 (*m)->set_position (is_start ? location->start() : location->end());
4308 MarkerDrag::update_item (Location*)
4313 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4315 , _fixed_grab_x (0.0)
4316 , _fixed_grab_y (0.0)
4317 , _cumulative_x_drag (0.0)
4318 , _cumulative_y_drag (0.0)
4322 if (_zero_gain_fraction < 0.0) {
4323 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4326 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4328 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4334 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4336 Drag::start_grab (event, _editor->cursors()->fader);
4338 // start the grab at the center of the control point so
4339 // the point doesn't 'jump' to the mouse after the first drag
4340 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4341 _fixed_grab_y = _point->get_y();
4343 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4344 setup_snap_delta (pos);
4346 float const fraction = 1 - (_point->get_y() / _point->line().height());
4347 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4349 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4351 if (!_point->can_slide ()) {
4352 _x_constrained = true;
4357 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4359 double dx = _drags->current_pointer_x() - last_pointer_x();
4360 double dy = current_pointer_y() - last_pointer_y();
4361 bool need_snap = true;
4363 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4369 /* coordinate in pixels relative to the start of the region (for region-based automation)
4370 or track (for track-based automation) */
4371 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4372 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4374 // calculate zero crossing point. back off by .01 to stay on the
4375 // positive side of zero
4376 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4378 if (_x_constrained) {
4381 if (_y_constrained) {
4385 _cumulative_x_drag = cx - _fixed_grab_x;
4386 _cumulative_y_drag = cy - _fixed_grab_y;
4390 cy = min ((double) _point->line().height(), cy);
4392 // make sure we hit zero when passing through
4393 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4397 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4398 if (!_x_constrained && need_snap) {
4399 _editor->snap_to_with_modifier (cx_frames, event);
4402 cx_frames -= snap_delta (event->button.state);
4403 cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset());
4405 float const fraction = 1.0 - (cy / _point->line().height());
4408 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4409 _editor->begin_reversible_command (_("automation event move"));
4410 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4412 pair<double, float> result;
4413 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4415 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4419 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4421 if (!movement_occurred) {
4424 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4425 _editor->reset_point_selection ();
4429 _point->line().end_drag (_pushing, _final_index);
4430 _editor->commit_reversible_command ();
4435 ControlPointDrag::aborted (bool)
4437 _point->line().reset ();
4441 ControlPointDrag::active (Editing::MouseMode m)
4443 if (m == Editing::MouseDraw) {
4444 /* always active in mouse draw */
4448 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4449 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4452 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4455 , _fixed_grab_x (0.0)
4456 , _fixed_grab_y (0.0)
4457 , _cumulative_y_drag (0)
4461 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4465 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4467 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4470 _item = &_line->grab_item ();
4472 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4473 origin, and ditto for y.
4476 double mx = event->button.x;
4477 double my = event->button.y;
4479 _line->grab_item().canvas_to_item (mx, my);
4481 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4483 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4484 /* no adjacent points */
4488 Drag::start_grab (event, _editor->cursors()->fader);
4490 /* store grab start in item frame */
4491 double const bx = _line->nth (_before)->get_x();
4492 double const ax = _line->nth (_after)->get_x();
4493 double const click_ratio = (ax - mx) / (ax - bx);
4495 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4500 double fraction = 1.0 - (cy / _line->height());
4502 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4506 LineDrag::motion (GdkEvent* event, bool first_move)
4508 double dy = current_pointer_y() - last_pointer_y();
4510 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4514 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4516 _cumulative_y_drag = cy - _fixed_grab_y;
4519 cy = min ((double) _line->height(), cy);
4521 double const fraction = 1.0 - (cy / _line->height());
4525 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4527 _editor->begin_reversible_command (_("automation range move"));
4528 _line->start_drag_line (_before, _after, initial_fraction);
4531 /* we are ignoring x position for this drag, so we can just pass in anything */
4532 pair<double, float> result;
4534 result = _line->drag_motion (0, fraction, true, false, ignored);
4535 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4539 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4541 if (movement_occurred) {
4542 motion (event, false);
4543 _line->end_drag (false, 0);
4544 _editor->commit_reversible_command ();
4546 /* add a new control point on the line */
4548 AutomationTimeAxisView* atv;
4550 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4551 framepos_t where = grab_frame ();
4554 double cy = _fixed_grab_y;
4556 _line->grab_item().item_to_canvas (cx, cy);
4558 atv->add_automation_event (event, where, cy, false);
4559 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4560 AudioRegionView* arv;
4562 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4563 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4570 LineDrag::aborted (bool)
4575 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4579 _region_view_grab_x (0.0),
4580 _cumulative_x_drag (0),
4584 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4588 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4590 Drag::start_grab (event);
4592 _line = reinterpret_cast<Line*> (_item);
4595 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4597 double cx = event->button.x;
4598 double cy = event->button.y;
4600 _item->parent()->canvas_to_item (cx, cy);
4602 /* store grab start in parent frame */
4603 _region_view_grab_x = cx;
4605 _before = *(float*) _item->get_data ("position");
4607 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4609 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4613 FeatureLineDrag::motion (GdkEvent*, bool)
4615 double dx = _drags->current_pointer_x() - last_pointer_x();
4617 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4619 _cumulative_x_drag += dx;
4621 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4630 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4632 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4634 float *pos = new float;
4637 _line->set_data ("position", pos);
4643 FeatureLineDrag::finished (GdkEvent*, bool)
4645 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4646 _arv->update_transient(_before, _before);
4650 FeatureLineDrag::aborted (bool)
4655 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4657 , _vertical_only (false)
4659 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4663 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4665 Drag::start_grab (event);
4666 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4670 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4677 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4679 framepos_t grab = grab_frame ();
4680 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4681 _editor->snap_to_with_modifier (grab, event);
4683 grab = raw_grab_frame ();
4686 /* base start and end on initial click position */
4696 if (current_pointer_y() < grab_y()) {
4697 y1 = current_pointer_y();
4700 y2 = current_pointer_y();
4704 if (start != end || y1 != y2) {
4706 double x1 = _editor->sample_to_pixel (start);
4707 double x2 = _editor->sample_to_pixel (end);
4708 const double min_dimension = 2.0;
4710 if (_vertical_only) {
4711 /* fixed 10 pixel width */
4715 x2 = min (x1 - min_dimension, x2);
4717 x2 = max (x1 + min_dimension, x2);
4722 y2 = min (y1 - min_dimension, y2);
4724 y2 = max (y1 + min_dimension, y2);
4727 /* translate rect into item space and set */
4729 ArdourCanvas::Rect r (x1, y1, x2, y2);
4731 /* this drag is a _trackview_only == true drag, so the y1 and
4732 * y2 (computed using current_pointer_y() and grab_y()) will be
4733 * relative to the top of the trackview group). The
4734 * rubberband rect has the same parent/scroll offset as the
4735 * the trackview group, so we can use the "r" rect directly
4736 * to set the shape of the rubberband.
4739 _editor->rubberband_rect->set (r);
4740 _editor->rubberband_rect->show();
4741 _editor->rubberband_rect->raise_to_top();
4743 show_verbose_cursor_time (pf);
4745 do_select_things (event, true);
4750 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4754 framepos_t grab = grab_frame ();
4755 framepos_t lpf = last_pointer_frame ();
4757 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4758 grab = raw_grab_frame ();
4759 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4773 if (current_pointer_y() < grab_y()) {
4774 y1 = current_pointer_y();
4777 y2 = current_pointer_y();
4781 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4785 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4787 if (movement_occurred) {
4789 motion (event, false);
4790 do_select_things (event, false);
4796 bool do_deselect = true;
4797 MidiTimeAxisView* mtv;
4799 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4801 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4802 /* nothing selected */
4803 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4804 do_deselect = false;
4808 /* do not deselect if Primary or Tertiary (toggle-select or
4809 * extend-select are pressed.
4812 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4813 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4820 _editor->rubberband_rect->hide();
4824 RubberbandSelectDrag::aborted (bool)
4826 _editor->rubberband_rect->hide ();
4829 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4830 : RegionDrag (e, i, p, v)
4832 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4836 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4838 Drag::start_grab (event, cursor);
4840 _editor->get_selection().add (_primary);
4842 framepos_t where = _primary->region()->position();
4843 setup_snap_delta (where);
4845 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4849 TimeFXDrag::motion (GdkEvent* event, bool)
4851 RegionView* rv = _primary;
4852 StreamView* cv = rv->get_time_axis_view().view ();
4854 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4855 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4856 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4857 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4858 _editor->snap_to_with_modifier (pf, event);
4859 pf -= snap_delta (event->button.state);
4861 if (pf > rv->region()->position()) {
4862 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4865 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4869 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4871 /* this may have been a single click, no drag. We still want the dialog
4872 to show up in that case, so that the user can manually edit the
4873 parameters for the timestretch.
4876 float fraction = 1.0;
4878 if (movement_occurred) {
4880 motion (event, false);
4882 _primary->get_time_axis_view().hide_timestretch ();
4884 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4886 if (adjusted_frame_pos < _primary->region()->position()) {
4887 /* backwards drag of the left edge - not usable */
4891 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4893 fraction = (double) newlen / (double) _primary->region()->length();
4895 #ifndef USE_RUBBERBAND
4896 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4897 if (_primary->region()->data_type() == DataType::AUDIO) {
4898 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4903 if (!_editor->get_selection().regions.empty()) {
4904 /* primary will already be included in the selection, and edit
4905 group shared editing will propagate selection across
4906 equivalent regions, so just use the current region
4910 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4911 error << _("An error occurred while executing time stretch operation") << endmsg;
4917 TimeFXDrag::aborted (bool)
4919 _primary->get_time_axis_view().hide_timestretch ();
4922 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4925 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4929 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4931 Drag::start_grab (event);
4935 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4937 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4941 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4943 if (movement_occurred && _editor->session()) {
4944 /* make sure we stop */
4945 _editor->session()->request_transport_speed (0.0);
4950 ScrubDrag::aborted (bool)
4955 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4959 , _time_selection_at_start (!_editor->get_selection().time.empty())
4961 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4963 if (_time_selection_at_start) {
4964 start_at_start = _editor->get_selection().time.start();
4965 end_at_start = _editor->get_selection().time.end_frame();
4970 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4972 if (_editor->session() == 0) {
4976 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4978 switch (_operation) {
4979 case CreateSelection:
4980 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4985 cursor = _editor->cursors()->selector;
4986 Drag::start_grab (event, cursor);
4989 case SelectionStartTrim:
4990 if (_editor->clicked_axisview) {
4991 _editor->clicked_axisview->order_selection_trims (_item, true);
4993 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4996 case SelectionEndTrim:
4997 if (_editor->clicked_axisview) {
4998 _editor->clicked_axisview->order_selection_trims (_item, false);
5000 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5004 Drag::start_grab (event, cursor);
5007 case SelectionExtend:
5008 Drag::start_grab (event, cursor);
5012 if (_operation == SelectionMove) {
5013 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5015 show_verbose_cursor_time (adjusted_current_frame (event));
5020 SelectionDrag::setup_pointer_frame_offset ()
5022 switch (_operation) {
5023 case CreateSelection:
5024 _pointer_frame_offset = 0;
5027 case SelectionStartTrim:
5029 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5032 case SelectionEndTrim:
5033 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5036 case SelectionExtend:
5042 SelectionDrag::motion (GdkEvent* event, bool first_move)
5044 framepos_t start = 0;
5046 framecnt_t length = 0;
5047 framecnt_t distance = 0;
5049 framepos_t const pending_position = adjusted_current_frame (event);
5051 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5055 switch (_operation) {
5056 case CreateSelection:
5058 framepos_t grab = grab_frame ();
5061 grab = adjusted_current_frame (event, false);
5062 if (grab < pending_position) {
5063 _editor->snap_to (grab, RoundDownMaybe);
5065 _editor->snap_to (grab, RoundUpMaybe);
5069 if (pending_position < grab) {
5070 start = pending_position;
5073 end = pending_position;
5077 /* first drag: Either add to the selection
5078 or create a new selection
5085 /* adding to the selection */
5086 _editor->set_selected_track_as_side_effect (Selection::Add);
5087 _editor->clicked_selection = _editor->selection->add (start, end);
5094 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5095 _editor->set_selected_track_as_side_effect (Selection::Set);
5098 _editor->clicked_selection = _editor->selection->set (start, end);
5102 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5103 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5104 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5106 _editor->selection->add (atest);
5110 /* select all tracks within the rectangle that we've marked out so far */
5111 TrackViewList new_selection;
5112 TrackViewList& all_tracks (_editor->track_views);
5114 ArdourCanvas::Coord const top = grab_y();
5115 ArdourCanvas::Coord const bottom = current_pointer_y();
5117 if (top >= 0 && bottom >= 0) {
5119 //first, find the tracks that are covered in the y range selection
5120 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5121 if ((*i)->covered_by_y_range (top, bottom)) {
5122 new_selection.push_back (*i);
5126 //now find any tracks that are GROUPED with the tracks we selected
5127 TrackViewList grouped_add = new_selection;
5128 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5129 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5130 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5131 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5132 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5133 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5134 grouped_add.push_back (*j);
5139 //now compare our list with the current selection, and add or remove as necessary
5140 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5141 TrackViewList tracks_to_add;
5142 TrackViewList tracks_to_remove;
5143 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5144 if ( !_editor->selection->tracks.contains ( *i ) )
5145 tracks_to_add.push_back ( *i );
5146 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5147 if ( !grouped_add.contains ( *i ) )
5148 tracks_to_remove.push_back ( *i );
5149 _editor->selection->add(tracks_to_add);
5150 _editor->selection->remove(tracks_to_remove);
5156 case SelectionStartTrim:
5158 end = _editor->selection->time[_editor->clicked_selection].end;
5160 if (pending_position > end) {
5163 start = pending_position;
5167 case SelectionEndTrim:
5169 start = _editor->selection->time[_editor->clicked_selection].start;
5171 if (pending_position < start) {
5174 end = pending_position;
5181 start = _editor->selection->time[_editor->clicked_selection].start;
5182 end = _editor->selection->time[_editor->clicked_selection].end;
5184 length = end - start;
5185 distance = pending_position - start;
5186 start = pending_position;
5187 _editor->snap_to (start);
5189 end = start + length;
5193 case SelectionExtend:
5198 switch (_operation) {
5200 if (_time_selection_at_start) {
5201 _editor->selection->move_time (distance);
5205 _editor->selection->replace (_editor->clicked_selection, start, end);
5209 if (_operation == SelectionMove) {
5210 show_verbose_cursor_time(start);
5212 show_verbose_cursor_time(pending_position);
5217 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5219 Session* s = _editor->session();
5221 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5222 if (movement_occurred) {
5223 motion (event, false);
5224 /* XXX this is not object-oriented programming at all. ick */
5225 if (_editor->selection->time.consolidate()) {
5226 _editor->selection->TimeChanged ();
5229 /* XXX what if its a music time selection? */
5231 if (s->get_play_range() && s->transport_rolling()) {
5232 s->request_play_range (&_editor->selection->time, true);
5233 } else if (!s->config.get_external_sync()) {
5234 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5235 if (_operation == SelectionEndTrim)
5236 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5238 s->request_locate (_editor->get_selection().time.start());
5242 if (_editor->get_selection().time.length() != 0) {
5243 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5245 s->clear_range_selection ();
5250 /* just a click, no pointer movement.
5253 if (_operation == SelectionExtend) {
5254 if (_time_selection_at_start) {
5255 framepos_t pos = adjusted_current_frame (event, false);
5256 framepos_t start = min (pos, start_at_start);
5257 framepos_t end = max (pos, end_at_start);
5258 _editor->selection->set (start, end);
5261 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5262 if (_editor->clicked_selection) {
5263 _editor->selection->remove (_editor->clicked_selection);
5266 if (!_editor->clicked_selection) {
5267 _editor->selection->clear_time();
5272 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5273 _editor->selection->set (_editor->clicked_axisview);
5276 if (s && s->get_play_range () && s->transport_rolling()) {
5277 s->request_stop (false, false);
5282 _editor->stop_canvas_autoscroll ();
5283 _editor->clicked_selection = 0;
5284 _editor->commit_reversible_selection_op ();
5288 SelectionDrag::aborted (bool)
5293 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5294 : Drag (e, i, false),
5298 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5300 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5301 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5302 physical_screen_height (_editor->current_toplevel()->get_window())));
5303 _drag_rect->hide ();
5305 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5306 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5309 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5311 /* normal canvas items will be cleaned up when their parent group is deleted. But
5312 this item is created as the child of a long-lived parent group, and so we
5313 need to explicitly delete it.
5319 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5321 if (_editor->session() == 0) {
5325 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5327 if (!_editor->temp_location) {
5328 _editor->temp_location = new Location (*_editor->session());
5331 switch (_operation) {
5332 case CreateSkipMarker:
5333 case CreateRangeMarker:
5334 case CreateTransportMarker:
5335 case CreateCDMarker:
5337 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5342 cursor = _editor->cursors()->selector;
5346 Drag::start_grab (event, cursor);
5348 show_verbose_cursor_time (adjusted_current_frame (event));
5352 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5354 framepos_t start = 0;
5356 ArdourCanvas::Rectangle *crect;
5358 switch (_operation) {
5359 case CreateSkipMarker:
5360 crect = _editor->range_bar_drag_rect;
5362 case CreateRangeMarker:
5363 crect = _editor->range_bar_drag_rect;
5365 case CreateTransportMarker:
5366 crect = _editor->transport_bar_drag_rect;
5368 case CreateCDMarker:
5369 crect = _editor->cd_marker_bar_drag_rect;
5372 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5377 framepos_t const pf = adjusted_current_frame (event);
5379 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5380 framepos_t grab = grab_frame ();
5381 _editor->snap_to (grab);
5383 if (pf < grab_frame()) {
5391 /* first drag: Either add to the selection
5392 or create a new selection.
5397 _editor->temp_location->set (start, end);
5401 update_item (_editor->temp_location);
5403 //_drag_rect->raise_to_top();
5409 _editor->temp_location->set (start, end);
5411 double x1 = _editor->sample_to_pixel (start);
5412 double x2 = _editor->sample_to_pixel (end);
5416 update_item (_editor->temp_location);
5419 show_verbose_cursor_time (pf);
5424 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5426 Location * newloc = 0;
5430 if (movement_occurred) {
5431 motion (event, false);
5434 switch (_operation) {
5435 case CreateSkipMarker:
5436 case CreateRangeMarker:
5437 case CreateCDMarker:
5439 XMLNode &before = _editor->session()->locations()->get_state();
5440 if (_operation == CreateSkipMarker) {
5441 _editor->begin_reversible_command (_("new skip marker"));
5442 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5443 flags = Location::IsRangeMarker | Location::IsSkip;
5444 _editor->range_bar_drag_rect->hide();
5445 } else if (_operation == CreateCDMarker) {
5446 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5447 _editor->begin_reversible_command (_("new CD marker"));
5448 flags = Location::IsRangeMarker | Location::IsCDMarker;
5449 _editor->cd_marker_bar_drag_rect->hide();
5451 _editor->begin_reversible_command (_("new skip marker"));
5452 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5453 flags = Location::IsRangeMarker;
5454 _editor->range_bar_drag_rect->hide();
5456 newloc = new Location (
5457 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5460 _editor->session()->locations()->add (newloc, true);
5461 XMLNode &after = _editor->session()->locations()->get_state();
5462 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5463 _editor->commit_reversible_command ();
5467 case CreateTransportMarker:
5468 // popup menu to pick loop or punch
5469 _editor->new_transport_marker_context_menu (&event->button, _item);
5475 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5477 if (_operation == CreateTransportMarker) {
5479 /* didn't drag, so just locate */
5481 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5483 } else if (_operation == CreateCDMarker) {
5485 /* didn't drag, but mark is already created so do
5488 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5493 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5495 if (end == max_framepos) {
5496 end = _editor->session()->current_end_frame ();
5499 if (start == max_framepos) {
5500 start = _editor->session()->current_start_frame ();
5503 switch (_editor->mouse_mode) {
5505 /* find the two markers on either side and then make the selection from it */
5506 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5510 /* find the two markers on either side of the click and make the range out of it */
5511 _editor->selection->set (start, end);
5520 _editor->stop_canvas_autoscroll ();
5524 RangeMarkerBarDrag::aborted (bool movement_occurred)
5526 if (movement_occurred) {
5527 _drag_rect->hide ();
5532 RangeMarkerBarDrag::update_item (Location* location)
5534 double const x1 = _editor->sample_to_pixel (location->start());
5535 double const x2 = _editor->sample_to_pixel (location->end());
5537 _drag_rect->set_x0 (x1);
5538 _drag_rect->set_x1 (x2);
5541 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5543 , _cumulative_dx (0)
5544 , _cumulative_dy (0)
5545 , _was_selected (false)
5547 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5549 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5551 _region = &_primary->region_view ();
5552 _note_height = _region->midi_stream_view()->note_height ();
5556 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5558 Drag::start_grab (event);
5559 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5561 if (!(_was_selected = _primary->selected())) {
5563 /* tertiary-click means extend selection - we'll do that on button release,
5564 so don't add it here, because otherwise we make it hard to figure
5565 out the "extend-to" range.
5568 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5571 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5574 _region->note_selected (_primary, true);
5576 _editor->get_selection().clear_points();
5577 _region->unique_select (_primary);
5583 /** @return Current total drag x change in frames */
5585 NoteDrag::total_dx (const guint state) const
5587 if (_x_constrained) {
5590 TempoMap& map (_editor->session()->tempo_map());
5593 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5595 /* primary note time */
5596 double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
5597 frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5599 /* new time of the primary note in session frames */
5600 frameoffset_t st = n + dx + snap_delta (state);
5602 framepos_t const rp = _region->region()->position ();
5604 /* prevent the note being dragged earlier than the region's position */
5607 /* possibly snap and return corresponding delta */
5611 if (ArdourKeyboard::indicates_snap (state)) {
5612 if (_editor->snap_mode () != SnapOff) {
5616 if (_editor->snap_mode () == SnapOff) {
5618 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5619 if (ArdourKeyboard::indicates_snap_delta (state)) {
5627 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5628 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5630 ret = st - n - snap_delta (state);
5635 /** @return Current total drag y change in note number */
5637 NoteDrag::total_dy () const
5639 if (_y_constrained) {
5643 MidiStreamView* msv = _region->midi_stream_view ();
5644 double const y = _region->midi_view()->y_position ();
5645 /* new current note */
5646 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5648 n = max (msv->lowest_note(), n);
5649 n = min (msv->highest_note(), n);
5650 /* and work out delta */
5651 return n - msv->y_to_note (grab_y() - y);
5655 NoteDrag::motion (GdkEvent * event, bool)
5657 /* Total change in x and y since the start of the drag */
5658 frameoffset_t const dx = total_dx (event->button.state);
5659 int8_t const dy = total_dy ();
5661 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5662 double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5663 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5666 _cumulative_dx += tdx;
5667 _cumulative_dy += tdy;
5669 int8_t note_delta = total_dy();
5672 _region->move_selection (tdx, tdy, note_delta);
5674 /* the new note value may be the same as the old one, but we
5675 * don't know what that means because the selection may have
5676 * involved more than one note and we might be doing something
5677 * odd with them. so show the note value anyway, always.
5680 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5682 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5688 NoteDrag::finished (GdkEvent* ev, bool moved)
5691 /* no motion - select note */
5693 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5694 _editor->current_mouse_mode() == Editing::MouseDraw) {
5696 bool changed = false;
5698 if (_was_selected) {
5699 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5701 _region->note_deselected (_primary);
5704 _editor->get_selection().clear_points();
5705 _region->unique_select (_primary);
5709 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5710 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5712 if (!extend && !add && _region->selection_size() > 1) {
5713 _editor->get_selection().clear_points();
5714 _region->unique_select (_primary);
5716 } else if (extend) {
5717 _region->note_selected (_primary, true, true);
5720 /* it was added during button press */
5727 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5728 _editor->commit_reversible_selection_op();
5732 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5737 NoteDrag::aborted (bool)
5742 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5743 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5744 : Drag (editor, atv->base_item ())
5746 , _y_origin (atv->y_position())
5747 , _nothing_to_drag (false)
5749 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5750 setup (atv->lines ());
5753 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5754 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5755 : Drag (editor, rv->get_canvas_group ())
5757 , _y_origin (rv->get_time_axis_view().y_position())
5758 , _nothing_to_drag (false)
5761 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5763 list<boost::shared_ptr<AutomationLine> > lines;
5765 AudioRegionView* audio_view;
5766 AutomationRegionView* automation_view;
5767 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5768 lines.push_back (audio_view->get_gain_line ());
5769 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5770 lines.push_back (automation_view->line ());
5773 error << _("Automation range drag created for invalid region type") << endmsg;
5779 /** @param lines AutomationLines to drag.
5780 * @param offset Offset from the session start to the points in the AutomationLines.
5783 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5785 /* find the lines that overlap the ranges being dragged */
5786 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5787 while (i != lines.end ()) {
5788 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5791 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5793 /* check this range against all the AudioRanges that we are using */
5794 list<AudioRange>::const_iterator k = _ranges.begin ();
5795 while (k != _ranges.end()) {
5796 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5802 /* add it to our list if it overlaps at all */
5803 if (k != _ranges.end()) {
5808 _lines.push_back (n);
5814 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5818 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5820 return 1.0 - ((global_y - _y_origin) / line->height());
5824 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5826 const double v = list->eval(x);
5827 return _integral ? rint(v) : v;
5831 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5833 Drag::start_grab (event, cursor);
5835 /* Get line states before we start changing things */
5836 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5837 i->state = &i->line->get_state ();
5838 i->original_fraction = y_fraction (i->line, current_pointer_y());
5841 if (_ranges.empty()) {
5843 /* No selected time ranges: drag all points */
5844 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5845 uint32_t const N = i->line->npoints ();
5846 for (uint32_t j = 0; j < N; ++j) {
5847 i->points.push_back (i->line->nth (j));
5853 if (_nothing_to_drag) {
5859 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5861 if (_nothing_to_drag && !first_move) {
5866 _editor->begin_reversible_command (_("automation range move"));
5868 if (!_ranges.empty()) {
5870 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5872 framecnt_t const half = (i->start + i->end) / 2;
5874 /* find the line that this audio range starts in */
5875 list<Line>::iterator j = _lines.begin();
5876 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5880 if (j != _lines.end()) {
5881 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5883 /* j is the line that this audio range starts in; fade into it;
5884 64 samples length plucked out of thin air.
5887 framepos_t a = i->start + 64;
5892 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5893 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5895 XMLNode &before = the_list->get_state();
5896 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5897 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5899 if (add_p || add_q) {
5900 _editor->session()->add_command (
5901 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5905 /* same thing for the end */
5908 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5912 if (j != _lines.end()) {
5913 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5915 /* j is the line that this audio range starts in; fade out of it;
5916 64 samples length plucked out of thin air.
5919 framepos_t b = i->end - 64;
5924 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5925 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5927 XMLNode &before = the_list->get_state();
5928 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5929 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5931 if (add_p || add_q) {
5932 _editor->session()->add_command (
5933 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5938 _nothing_to_drag = true;
5940 /* Find all the points that should be dragged and put them in the relevant
5941 points lists in the Line structs.
5944 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5946 uint32_t const N = i->line->npoints ();
5947 for (uint32_t j = 0; j < N; ++j) {
5949 /* here's a control point on this line */
5950 ControlPoint* p = i->line->nth (j);
5951 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5953 /* see if it's inside a range */
5954 list<AudioRange>::const_iterator k = _ranges.begin ();
5955 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5959 if (k != _ranges.end()) {
5960 /* dragging this point */
5961 _nothing_to_drag = false;
5962 i->points.push_back (p);
5968 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5969 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5973 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5974 float const f = y_fraction (l->line, current_pointer_y());
5975 /* we are ignoring x position for this drag, so we can just pass in anything */
5976 pair<double, float> result;
5978 result = l->line->drag_motion (0, f, true, false, ignored);
5979 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5984 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5986 if (_nothing_to_drag || !motion_occurred) {
5990 motion (event, false);
5991 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5992 i->line->end_drag (false, 0);
5995 _editor->commit_reversible_command ();
5999 AutomationRangeDrag::aborted (bool)
6001 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6006 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6008 , initial_time_axis_view (itav)
6010 /* note that time_axis_view may be null if the regionview was created
6011 * as part of a copy operation.
6013 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6014 layer = v->region()->layer ();
6015 initial_y = v->get_canvas_group()->position().y;
6016 initial_playlist = v->region()->playlist ();
6017 initial_position = v->region()->position ();
6018 initial_end = v->region()->position () + v->region()->length ();
6021 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6022 : Drag (e, i->canvas_item ())
6025 , _cumulative_dx (0)
6027 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6028 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6033 PatchChangeDrag::motion (GdkEvent* ev, bool)
6035 framepos_t f = adjusted_current_frame (ev);
6036 boost::shared_ptr<Region> r = _region_view->region ();
6037 f = max (f, r->position ());
6038 f = min (f, r->last_frame ());
6040 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6041 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6042 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6043 _cumulative_dx = dxu;
6047 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6049 if (!movement_occurred) {
6050 if (was_double_click()) {
6051 _region_view->edit_patch_change (_patch_change);
6056 boost::shared_ptr<Region> r (_region_view->region ());
6057 framepos_t f = adjusted_current_frame (ev);
6058 f = max (f, r->position ());
6059 f = min (f, r->last_frame ());
6061 _region_view->move_patch_change (
6063 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6068 PatchChangeDrag::aborted (bool)
6070 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6074 PatchChangeDrag::setup_pointer_frame_offset ()
6076 boost::shared_ptr<Region> region = _region_view->region ();
6077 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6080 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6081 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6088 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6090 _region_view->update_drag_selection (
6092 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6096 MidiRubberbandSelectDrag::deselect_things ()
6101 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6102 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6105 _vertical_only = true;
6109 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6111 double const y = _region_view->midi_view()->y_position ();
6113 y1 = max (0.0, y1 - y);
6114 y2 = max (0.0, y2 - y);
6116 _region_view->update_vertical_drag_selection (
6119 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6124 MidiVerticalSelectDrag::deselect_things ()
6129 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6130 : RubberbandSelectDrag (e, i)
6136 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6138 if (drag_in_progress) {
6139 /* We just want to select things at the end of the drag, not during it */
6143 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6145 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6147 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6149 _editor->commit_reversible_selection_op ();
6153 EditorRubberbandSelectDrag::deselect_things ()
6155 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6157 _editor->selection->clear_tracks();
6158 _editor->selection->clear_regions();
6159 _editor->selection->clear_points ();
6160 _editor->selection->clear_lines ();
6161 _editor->selection->clear_midi_notes ();
6163 _editor->commit_reversible_selection_op();
6166 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6171 _note[0] = _note[1] = 0;
6174 NoteCreateDrag::~NoteCreateDrag ()
6180 NoteCreateDrag::grid_frames (framepos_t t) const
6183 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6185 grid_beats = Evoral::Beats(1);
6188 return _region_view->region_beats_to_region_frames (grid_beats);
6192 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6194 Drag::start_grab (event, cursor);
6196 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6197 TempoMap& map (_editor->session()->tempo_map());
6199 const framepos_t pf = _drags->current_pointer_frame ();
6200 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6202 double eqaf = map.exact_qn_at_frame (pf, divisions);
6204 if (divisions != 0) {
6205 bool success = false;
6206 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf);
6208 grid_beats = Evoral::Beats(1);
6211 const double qaf = map.quarter_note_at_frame (pf);
6213 /* Hack so that we always snap to the note that we are over, instead of snapping
6214 to the next one if we're more than halfway through the one we're over.
6217 const double rem = eqaf - qaf;
6219 eqaf -= grid_beats.to_double();
6223 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6224 _note[1] = _note[0];
6226 MidiStreamView* sv = _region_view->midi_stream_view ();
6227 double const x = _editor->sample_to_pixel (_note[0]);
6228 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6230 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6231 _drag_rect->set_outline_all ();
6232 _drag_rect->set_outline_color (0xffffff99);
6233 _drag_rect->set_fill_color (0xffffff66);
6237 NoteCreateDrag::motion (GdkEvent* event, bool)
6239 TempoMap& map (_editor->session()->tempo_map());
6240 const framepos_t pf = _drags->current_pointer_frame ();
6241 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6242 double eqaf = map.exact_qn_at_frame (pf, divisions);
6244 if (divisions != 0) {
6245 bool success = false;
6246 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf);
6248 grid_beats = Evoral::Beats(1);
6251 const double qaf = map.quarter_note_at_frame (pf);
6252 /* Hack so that we always snap to the note that we are over, instead of snapping
6253 to the next one if we're more than halfway through the one we're over.
6256 const double rem = eqaf - qaf;
6258 eqaf -= grid_beats.to_double();
6261 eqaf += grid_beats.to_double();
6263 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6265 double const x0 = _editor->sample_to_pixel (_note[0]);
6266 double const x1 = _editor->sample_to_pixel (_note[1]);
6267 _drag_rect->set_x0 (std::min(x0, x1));
6268 _drag_rect->set_x1 (std::max(x0, x1));
6272 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6274 if (!had_movement) {
6278 framepos_t const start = min (_note[0], _note[1]);
6279 framepos_t const start_sess_rel = start + _region_view->region()->position();
6280 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6282 framecnt_t const g = grid_frames (start);
6283 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6285 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6289 TempoMap& map (_editor->session()->tempo_map());
6290 const double qn_length = map.quarter_note_at_frame (start_sess_rel + length) - map.quarter_note_at_frame (start_sess_rel);
6292 Evoral::Beats qn_length_beats = max (one_tick, Evoral::Beats (qn_length));
6293 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6297 NoteCreateDrag::y_to_region (double y) const
6300 _region_view->get_canvas_group()->canvas_to_item (x, y);
6305 NoteCreateDrag::aborted (bool)
6310 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6315 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6319 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6321 Drag::start_grab (event, cursor);
6325 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6331 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6334 distance = _drags->current_pointer_x() - grab_x();
6335 len = ar->fade_in()->back()->when;
6337 distance = grab_x() - _drags->current_pointer_x();
6338 len = ar->fade_out()->back()->when;
6341 /* how long should it be ? */
6343 new_length = len + _editor->pixel_to_sample (distance);
6345 /* now check with the region that this is legal */
6347 new_length = ar->verify_xfade_bounds (new_length, start);
6350 arv->reset_fade_in_shape_width (ar, new_length);
6352 arv->reset_fade_out_shape_width (ar, new_length);
6357 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6363 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6366 distance = _drags->current_pointer_x() - grab_x();
6367 len = ar->fade_in()->back()->when;
6369 distance = grab_x() - _drags->current_pointer_x();
6370 len = ar->fade_out()->back()->when;
6373 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6375 _editor->begin_reversible_command ("xfade trim");
6376 ar->playlist()->clear_owned_changes ();
6379 ar->set_fade_in_length (new_length);
6381 ar->set_fade_out_length (new_length);
6384 /* Adjusting the xfade may affect other regions in the playlist, so we need
6385 to get undo Commands from the whole playlist rather than just the
6389 vector<Command*> cmds;
6390 ar->playlist()->rdiff (cmds);
6391 _editor->session()->add_commands (cmds);
6392 _editor->commit_reversible_command ();
6397 CrossfadeEdgeDrag::aborted (bool)
6400 // arv->redraw_start_xfade ();
6402 // arv->redraw_end_xfade ();
6406 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6407 : Drag (e, item, true)
6408 , line (new EditorCursor (*e))
6410 line->set_position (pos);
6414 RegionCutDrag::~RegionCutDrag ()
6420 RegionCutDrag::motion (GdkEvent*, bool)
6422 framepos_t where = _drags->current_pointer_frame();
6423 _editor->snap_to (where);
6425 line->set_position (where);
6429 RegionCutDrag::finished (GdkEvent* event, bool)
6431 _editor->get_track_canvas()->canvas()->re_enter();
6433 framepos_t pos = _drags->current_pointer_frame();
6437 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6443 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state));
6447 RegionCutDrag::aborted (bool)
6451 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6452 : Drag (e, item, true)
6457 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6459 Drag::start_grab (event, c);
6461 framepos_t where = _editor->canvas_event_sample(event);
6463 _editor->_dragging_playhead = true;
6465 _editor->playhead_cursor->set_position (where);
6469 RulerZoomDrag::motion (GdkEvent* event, bool)
6471 framepos_t where = _editor->canvas_event_sample(event);
6473 _editor->playhead_cursor->set_position (where);
6475 const double movement_limit = 20.0;
6476 const double scale = 1.08;
6477 const double y_delta = last_pointer_y() - current_pointer_y();
6479 if (y_delta > 0 && y_delta < movement_limit) {
6480 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6481 } else if (y_delta < 0 && y_delta > -movement_limit) {
6482 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6487 RulerZoomDrag::finished (GdkEvent*, bool)
6489 _editor->_dragging_playhead = false;
6491 Session* s = _editor->session ();
6493 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6494 _editor->_pending_locate_request = true;
6500 RulerZoomDrag::aborted (bool)