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 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1534 RegionView* rv = i->view;
1535 RouteTimeAxisView* dest_rtv = 0;
1537 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1542 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1543 /* dragged to drop zone */
1545 PlaylistMapping::iterator pm;
1547 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1548 /* first region from this original playlist: create a new track */
1549 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1550 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1551 dest_rtv = new_time_axis_view;
1553 /* we already created a new track for regions from this playlist, use it */
1554 dest_rtv = pm->second;
1558 /* destination time axis view is the one we dragged to */
1559 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1564 double const dest_layer = i->layer;
1566 views_to_update.insert (dest_rtv);
1570 if (changed_position && !_x_constrained) {
1571 where = rv->region()->position() - drag_delta;
1573 where = rv->region()->position();
1576 if (changed_tracks) {
1578 /* insert into new playlist */
1580 RegionView* new_view = insert_region_into_playlist (
1581 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1582 modified_playlists, _editor->get_grid_music_divisions (ev_state)
1585 if (new_view == 0) {
1590 new_views.push_back (new_view);
1592 /* remove from old playlist */
1594 /* the region that used to be in the old playlist is not
1595 moved to the new one - we use a copy of it. as a result,
1596 any existing editor for the region should no longer be
1599 rv->hide_region_editor();
1602 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1606 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1608 /* this movement may result in a crossfade being modified, or a layering change,
1609 so we need to get undo data from the playlist as well as the region.
1612 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1614 playlist->clear_changes ();
1617 rv->region()->clear_changes ();
1620 motion on the same track. plonk the previously reparented region
1621 back to its original canvas group (its streamview).
1622 No need to do anything for copies as they are fake regions which will be deleted.
1625 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1626 rv->get_canvas_group()->set_y_position (i->initial_y);
1629 /* just change the model */
1630 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1631 playlist->set_layer (rv->region(), dest_layer);
1634 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1636 r = frozen_playlists.insert (playlist);
1639 playlist->freeze ();
1642 rv->region()->set_position (where, _editor->get_grid_music_divisions (ev_state));
1643 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1646 if (changed_tracks) {
1648 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1649 was selected in all of them, then removing it from a playlist will have removed all
1650 trace of it from _views (i.e. there were N regions selected, we removed 1,
1651 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1652 corresponding regionview, and _views is now empty).
1654 This could have invalidated any and all iterators into _views.
1656 The heuristic we use here is: if the region selection is empty, break out of the loop
1657 here. if the region selection is not empty, then restart the loop because we know that
1658 we must have removed at least the region(view) we've just been working on as well as any
1659 that we processed on previous iterations.
1661 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1662 we can just iterate.
1666 if (_views.empty()) {
1677 /* If we've created new regions either by copying or moving
1678 to a new track, we want to replace the old selection with the new ones
1681 if (new_views.size() > 0) {
1682 _editor->selection->set (new_views);
1685 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1689 /* write commands for the accumulated diffs for all our modified playlists */
1690 add_stateful_diff_commands_for_playlists (modified_playlists);
1691 /* applies to _brushing */
1692 _editor->commit_reversible_command ();
1694 /* We have futzed with the layering of canvas items on our streamviews.
1695 If any region changed layer, this will have resulted in the stream
1696 views being asked to set up their region views, and all will be well.
1697 If not, we might now have badly-ordered region views. Ask the StreamViews
1698 involved to sort themselves out, just in case.
1701 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1702 (*i)->view()->playlist_layered ((*i)->track ());
1706 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1707 * @param region Region to remove.
1708 * @param playlist playlist To remove from.
1709 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1710 * that clear_changes () is only called once per playlist.
1713 RegionMoveDrag::remove_region_from_playlist (
1714 boost::shared_ptr<Region> region,
1715 boost::shared_ptr<Playlist> playlist,
1716 PlaylistSet& modified_playlists
1719 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1722 playlist->clear_changes ();
1725 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1729 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1730 * clearing the playlist's diff history first if necessary.
1731 * @param region Region to insert.
1732 * @param dest_rtv Destination RouteTimeAxisView.
1733 * @param dest_layer Destination layer.
1734 * @param where Destination position.
1735 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1736 * that clear_changes () is only called once per playlist.
1737 * @return New RegionView, or 0 if no insert was performed.
1740 RegionMoveDrag::insert_region_into_playlist (
1741 boost::shared_ptr<Region> region,
1742 RouteTimeAxisView* dest_rtv,
1745 PlaylistSet& modified_playlists,
1746 const int32_t sub_num
1749 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1750 if (!dest_playlist) {
1754 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1755 _new_region_view = 0;
1756 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1758 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1759 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1761 dest_playlist->clear_changes ();
1763 dest_playlist->add_region (region, where, 1.0, false, sub_num);
1765 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1766 dest_playlist->set_layer (region, dest_layer);
1771 assert (_new_region_view);
1773 return _new_region_view;
1777 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1779 _new_region_view = rv;
1783 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1785 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1786 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1788 _editor->session()->add_command (c);
1797 RegionMoveDrag::aborted (bool movement_occurred)
1801 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1802 list<DraggingView>::const_iterator next = i;
1811 RegionMotionDrag::aborted (movement_occurred);
1816 RegionMotionDrag::aborted (bool)
1818 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1820 StreamView* sview = (*i)->view();
1823 if (sview->layer_display() == Expanded) {
1824 sview->set_layer_display (Stacked);
1829 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1830 RegionView* rv = i->view;
1831 TimeAxisView* tv = &(rv->get_time_axis_view ());
1832 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1834 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1835 rv->get_canvas_group()->set_y_position (0);
1837 rv->move (-_total_x_delta, 0);
1838 rv->set_height (rtv->view()->child_height ());
1842 /** @param b true to brush, otherwise false.
1843 * @param c true to make copies of the regions being moved, otherwise false.
1845 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1846 : RegionMotionDrag (e, i, p, v, b)
1848 , _new_region_view (0)
1850 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1853 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1854 if (rtv && rtv->is_track()) {
1855 speed = rtv->track()->speed ();
1858 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1862 RegionMoveDrag::setup_pointer_frame_offset ()
1864 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1867 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1868 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1870 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1872 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1873 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1875 _primary = v->view()->create_region_view (r, false, false);
1877 _primary->get_canvas_group()->show ();
1878 _primary->set_position (pos, 0);
1879 _views.push_back (DraggingView (_primary, this, v));
1881 _last_frame_position = pos;
1883 _item = _primary->get_canvas_group ();
1887 RegionInsertDrag::finished (GdkEvent * event, bool)
1889 int pos = _views.front().time_axis_view;
1890 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1892 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1894 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1895 _primary->get_canvas_group()->set_y_position (0);
1897 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1899 _editor->begin_reversible_command (Operations::insert_region);
1900 playlist->clear_changes ();
1901 playlist->add_region (_primary->region (), _last_frame_position);
1903 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1904 if (Config->get_edit_mode() == Ripple) {
1905 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1908 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1909 _editor->commit_reversible_command ();
1917 RegionInsertDrag::aborted (bool)
1924 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1925 : RegionMoveDrag (e, i, p, v, false, false)
1927 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1930 struct RegionSelectionByPosition {
1931 bool operator() (RegionView*a, RegionView* b) {
1932 return a->region()->position () < b->region()->position();
1937 RegionSpliceDrag::motion (GdkEvent* event, bool)
1939 /* Which trackview is this ? */
1941 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1942 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1944 /* The region motion is only processed if the pointer is over
1948 if (!tv || !tv->is_track()) {
1949 /* To make sure we hide the verbose canvas cursor when the mouse is
1950 not held over an audio track.
1952 _editor->verbose_cursor()->hide ();
1955 _editor->verbose_cursor()->show ();
1960 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1966 RegionSelection copy;
1967 _editor->selection->regions.by_position(copy);
1969 framepos_t const pf = adjusted_current_frame (event);
1971 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1973 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1979 boost::shared_ptr<Playlist> playlist;
1981 if ((playlist = atv->playlist()) == 0) {
1985 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1990 if (pf < (*i)->region()->last_frame() + 1) {
1994 if (pf > (*i)->region()->first_frame()) {
2000 playlist->shuffle ((*i)->region(), dir);
2005 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2007 RegionMoveDrag::finished (event, movement_occurred);
2011 RegionSpliceDrag::aborted (bool)
2021 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2024 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2026 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2027 RegionSelection to_ripple;
2028 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2029 if ((*i)->position() >= where) {
2030 to_ripple.push_back (rtv->view()->find_view(*i));
2034 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2035 if (!exclude.contains (*i)) {
2036 // the selection has already been added to _views
2038 if (drag_in_progress) {
2039 // do the same things that RegionMotionDrag::motion does when
2040 // first_move is true, for the region views that we're adding
2041 // to _views this time
2044 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2045 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2046 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2047 rvg->reparent (_editor->_drag_motion_group);
2049 // we only need to move in the y direction
2050 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2055 _views.push_back (DraggingView (*i, this, tav));
2061 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2064 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2065 // we added all the regions after the selection
2067 std::list<DraggingView>::iterator to_erase = i++;
2068 if (!_editor->selection->regions.contains (to_erase->view)) {
2069 // restore the non-selected regions to their original playlist & positions,
2070 // and then ripple them back by the length of the regions that were dragged away
2071 // do the same things as RegionMotionDrag::aborted
2073 RegionView *rv = to_erase->view;
2074 TimeAxisView* tv = &(rv->get_time_axis_view ());
2075 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2078 // plonk them back onto their own track
2079 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2080 rv->get_canvas_group()->set_y_position (0);
2084 // move the underlying region to match the view
2085 rv->region()->set_position (rv->region()->position() + amount);
2087 // restore the view to match the underlying region's original position
2088 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2091 rv->set_height (rtv->view()->child_height ());
2092 _views.erase (to_erase);
2098 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2100 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2102 return allow_moves_across_tracks;
2110 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2111 : RegionMoveDrag (e, i, p, v, false, false)
2113 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2114 // compute length of selection
2115 RegionSelection selected_regions = _editor->selection->regions;
2116 selection_length = selected_regions.end_frame() - selected_regions.start();
2118 // we'll only allow dragging to another track in ripple mode if all the regions
2119 // being dragged start off on the same track
2120 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2123 exclude = new RegionList;
2124 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2125 exclude->push_back((*i)->region());
2128 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2129 RegionSelection copy;
2130 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2132 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2133 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2135 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2136 // find ripple start point on each applicable playlist
2137 RegionView *first_selected_on_this_track = NULL;
2138 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2139 if ((*i)->region()->playlist() == (*pi)) {
2140 // region is on this playlist - it's the first, because they're sorted
2141 first_selected_on_this_track = *i;
2145 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2146 add_all_after_to_views (
2147 &first_selected_on_this_track->get_time_axis_view(),
2148 first_selected_on_this_track->region()->position(),
2149 selected_regions, false);
2152 if (allow_moves_across_tracks) {
2153 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2161 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2163 /* Which trackview is this ? */
2165 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2166 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2168 /* The region motion is only processed if the pointer is over
2172 if (!tv || !tv->is_track()) {
2173 /* To make sure we hide the verbose canvas cursor when the mouse is
2174 not held over an audiotrack.
2176 _editor->verbose_cursor()->hide ();
2180 framepos_t where = adjusted_current_frame (event);
2181 assert (where >= 0);
2183 double delta = compute_x_delta (event, &after);
2185 framecnt_t amount = _editor->pixel_to_sample (delta);
2187 if (allow_moves_across_tracks) {
2188 // all the originally selected regions were on the same track
2190 framecnt_t adjust = 0;
2191 if (prev_tav && tv != prev_tav) {
2192 // dragged onto a different track
2193 // remove the unselected regions from _views, restore them to their original positions
2194 // and add the regions after the drop point on the new playlist to _views instead.
2195 // undo the effect of rippling the previous playlist, and include the effect of removing
2196 // the dragged region(s) from this track
2198 remove_unselected_from_views (prev_amount, false);
2199 // ripple previous playlist according to the regions that have been removed onto the new playlist
2200 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2203 // move just the selected regions
2204 RegionMoveDrag::motion(event, first_move);
2206 // ensure that the ripple operation on the new playlist inserts selection_length time
2207 adjust = selection_length;
2208 // ripple the new current playlist
2209 tv->playlist()->ripple (where, amount+adjust, exclude);
2211 // add regions after point where drag entered this track to subsequent ripples
2212 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2215 // motion on same track
2216 RegionMoveDrag::motion(event, first_move);
2220 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2221 prev_position = where;
2223 // selection encompasses multiple tracks - just drag
2224 // cross-track drags are forbidden
2225 RegionMoveDrag::motion(event, first_move);
2228 if (!_x_constrained) {
2229 prev_amount += amount;
2232 _last_frame_position = after;
2236 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2238 if (!movement_occurred) {
2242 if (was_double_click() && !_views.empty()) {
2243 DraggingView dv = _views.front();
2244 dv.view->show_region_editor ();
2251 _editor->begin_reversible_command(_("Ripple drag"));
2253 // remove the regions being rippled from the dragging view, updating them to
2254 // their new positions
2255 remove_unselected_from_views (prev_amount, true);
2257 if (allow_moves_across_tracks) {
2259 // if regions were dragged across tracks, we've rippled any later
2260 // regions on the track the regions were dragged off, so we need
2261 // to add the original track to the undo record
2262 orig_tav->playlist()->clear_changes();
2263 vector<Command*> cmds;
2264 orig_tav->playlist()->rdiff (cmds);
2265 _editor->session()->add_commands (cmds);
2267 if (prev_tav && prev_tav != orig_tav) {
2268 prev_tav->playlist()->clear_changes();
2269 vector<Command*> cmds;
2270 prev_tav->playlist()->rdiff (cmds);
2271 _editor->session()->add_commands (cmds);
2274 // selection spanned multiple tracks - all will need adding to undo record
2276 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2277 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2279 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2280 (*pi)->clear_changes();
2281 vector<Command*> cmds;
2282 (*pi)->rdiff (cmds);
2283 _editor->session()->add_commands (cmds);
2287 // other modified playlists are added to undo by RegionMoveDrag::finished()
2288 RegionMoveDrag::finished (event, movement_occurred);
2289 _editor->commit_reversible_command();
2293 RegionRippleDrag::aborted (bool movement_occurred)
2295 RegionMoveDrag::aborted (movement_occurred);
2300 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2302 _view (dynamic_cast<MidiTimeAxisView*> (v))
2304 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2310 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2313 _editor->begin_reversible_command (_("create region"));
2314 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2315 _view->playlist()->freeze ();
2318 framepos_t const f = adjusted_current_frame (event);
2319 if (f < grab_frame()) {
2320 _region->set_initial_position (f);
2323 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2324 so that if this region is duplicated, its duplicate starts on
2325 a snap point rather than 1 frame after a snap point. Otherwise things get
2326 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2327 place snapped notes at the start of the region.
2330 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2331 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2337 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2339 if (!movement_occurred) {
2340 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2342 _view->playlist()->thaw ();
2343 _editor->commit_reversible_command();
2348 RegionCreateDrag::aborted (bool)
2351 _view->playlist()->thaw ();
2357 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2362 , _was_selected (false)
2365 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2369 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2371 Gdk::Cursor* cursor;
2372 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2374 float x_fraction = cnote->mouse_x_fraction ();
2376 if (x_fraction > 0.0 && x_fraction < 0.25) {
2377 cursor = _editor->cursors()->left_side_trim;
2380 cursor = _editor->cursors()->right_side_trim;
2384 Drag::start_grab (event, cursor);
2386 region = &cnote->region_view();
2389 temp = region->snap_to_pixel (cnote->x0 (), true);
2390 _snap_delta = temp - cnote->x0 ();
2394 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2399 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2400 if (ms.size() > 1) {
2401 /* has to be relative, may make no sense otherwise */
2405 if (!(_was_selected = cnote->selected())) {
2407 /* tertiary-click means extend selection - we'll do that on button release,
2408 so don't add it here, because otherwise we make it hard to figure
2409 out the "extend-to" range.
2412 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2415 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2418 region->note_selected (cnote, true);
2420 _editor->get_selection().clear_points();
2421 region->unique_select (cnote);
2428 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2430 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2432 _editor->begin_reversible_command (_("resize notes"));
2434 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2435 MidiRegionSelection::iterator next;
2438 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2440 mrv->begin_resizing (at_front);
2446 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2447 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2449 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2453 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2455 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2456 if (_editor->snap_mode () != SnapOff) {
2460 if (_editor->snap_mode () == SnapOff) {
2462 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2463 if (apply_snap_delta) {
2469 if (apply_snap_delta) {
2473 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2479 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2481 if (!movement_occurred) {
2482 /* no motion - select note */
2483 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2484 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2485 _editor->current_mouse_mode() == Editing::MouseDraw) {
2487 bool changed = false;
2489 if (_was_selected) {
2490 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2492 region->note_deselected (cnote);
2495 _editor->get_selection().clear_points();
2496 region->unique_select (cnote);
2500 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2501 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2503 if (!extend && !add && region->selection_size() > 1) {
2504 _editor->get_selection().clear_points();
2505 region->unique_select (cnote);
2507 } else if (extend) {
2508 region->note_selected (cnote, true, true);
2511 /* it was added during button press */
2517 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2518 _editor->commit_reversible_selection_op();
2525 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2526 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2527 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2529 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2532 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2534 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2535 if (_editor->snap_mode () != SnapOff) {
2539 if (_editor->snap_mode () == SnapOff) {
2541 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2542 if (apply_snap_delta) {
2548 if (apply_snap_delta) {
2552 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2556 _editor->commit_reversible_command ();
2560 NoteResizeDrag::aborted (bool)
2562 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2563 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2564 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2566 mrv->abort_resizing ();
2571 AVDraggingView::AVDraggingView (RegionView* v)
2574 initial_position = v->region()->position ();
2577 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2580 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2583 TrackViewList empty;
2585 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2586 std::list<RegionView*> views = rs.by_layer();
2589 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2590 RegionView* rv = (*i);
2591 if (!rv->region()->video_locked()) {
2594 if (rv->region()->locked()) {
2597 _views.push_back (AVDraggingView (rv));
2602 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2604 Drag::start_grab (event);
2605 if (_editor->session() == 0) {
2609 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2615 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2619 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2620 _max_backwards_drag = (
2621 ARDOUR_UI::instance()->video_timeline->get_duration()
2622 + ARDOUR_UI::instance()->video_timeline->get_offset()
2623 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2626 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2627 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2628 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2631 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2634 Timecode::Time timecode;
2635 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2636 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);
2637 show_verbose_cursor_text (buf);
2641 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2643 if (_editor->session() == 0) {
2646 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2650 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2654 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2655 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2657 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2658 dt = - _max_backwards_drag;
2661 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2662 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2664 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2665 RegionView* rv = i->view;
2666 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2669 rv->region()->clear_changes ();
2670 rv->region()->suspend_property_changes();
2672 rv->region()->set_position(i->initial_position + dt);
2673 rv->region_changed(ARDOUR::Properties::position);
2676 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2677 Timecode::Time timecode;
2678 Timecode::Time timediff;
2680 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2681 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2682 snprintf (buf, sizeof (buf),
2683 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2684 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2685 , _("Video Start:"),
2686 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2688 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2690 show_verbose_cursor_text (buf);
2694 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2696 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2703 if (!movement_occurred || ! _editor->session()) {
2707 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2709 _editor->begin_reversible_command (_("Move Video"));
2711 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2712 ARDOUR_UI::instance()->video_timeline->save_undo();
2713 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2714 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2716 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2717 i->view->drag_end();
2718 i->view->region()->resume_property_changes ();
2720 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2723 _editor->session()->maybe_update_session_range(
2724 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2725 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2729 _editor->commit_reversible_command ();
2733 VideoTimeLineDrag::aborted (bool)
2735 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2738 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2739 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2741 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2742 i->view->region()->resume_property_changes ();
2743 i->view->region()->set_position(i->initial_position);
2747 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2748 : RegionDrag (e, i, p, v)
2749 , _operation (StartTrim)
2750 , _preserve_fade_anchor (preserve_fade_anchor)
2751 , _jump_position_when_done (false)
2753 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2757 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2760 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2761 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2763 if (tv && tv->is_track()) {
2764 speed = tv->track()->speed();
2767 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2768 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2769 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2771 framepos_t const pf = adjusted_current_frame (event);
2772 setup_snap_delta (region_start);
2774 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2775 /* Move the contents of the region around without changing the region bounds */
2776 _operation = ContentsTrim;
2777 Drag::start_grab (event, _editor->cursors()->trimmer);
2779 /* These will get overridden for a point trim.*/
2780 if (pf < (region_start + region_length/2)) {
2781 /* closer to front */
2782 _operation = StartTrim;
2783 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2784 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2786 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2790 _operation = EndTrim;
2791 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2792 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2794 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2798 /* jump trim disabled for now
2799 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2800 _jump_position_when_done = true;
2804 switch (_operation) {
2806 show_verbose_cursor_time (region_start);
2809 show_verbose_cursor_duration (region_start, region_end);
2812 show_verbose_cursor_time (pf);
2816 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2817 i->view->region()->suspend_property_changes ();
2822 TrimDrag::motion (GdkEvent* event, bool first_move)
2824 RegionView* rv = _primary;
2827 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2828 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2829 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2830 frameoffset_t frame_delta = 0;
2832 if (tv && tv->is_track()) {
2833 speed = tv->track()->speed();
2835 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2836 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2842 switch (_operation) {
2844 trim_type = "Region start trim";
2847 trim_type = "Region end trim";
2850 trim_type = "Region content trim";
2857 _editor->begin_reversible_command (trim_type);
2859 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2860 RegionView* rv = i->view;
2861 rv->region()->playlist()->clear_owned_changes ();
2863 if (_operation == StartTrim) {
2864 rv->trim_front_starting ();
2867 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2870 arv->temporarily_hide_envelope ();
2874 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2875 insert_result = _editor->motion_frozen_playlists.insert (pl);
2877 if (insert_result.second) {
2883 bool non_overlap_trim = false;
2885 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2886 non_overlap_trim = true;
2889 /* contstrain trim to fade length */
2890 if (_preserve_fade_anchor) {
2891 switch (_operation) {
2893 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2894 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2896 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2897 if (ar->locked()) continue;
2898 framecnt_t len = ar->fade_in()->back()->when;
2899 if (len < dt) dt = min(dt, len);
2903 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2904 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2906 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2907 if (ar->locked()) continue;
2908 framecnt_t len = ar->fade_out()->back()->when;
2909 if (len < -dt) dt = max(dt, -len);
2918 switch (_operation) {
2920 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2921 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2922 , _editor->get_grid_music_divisions (event->button.state));
2924 if (changed && _preserve_fade_anchor) {
2925 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2927 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2928 framecnt_t len = ar->fade_in()->back()->when;
2929 framecnt_t diff = ar->first_frame() - i->initial_position;
2930 framepos_t new_length = len - diff;
2931 i->anchored_fade_length = min (ar->length(), new_length);
2932 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2933 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2940 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2941 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, _editor->get_grid_music_divisions (event->button.state));
2942 if (changed && _preserve_fade_anchor) {
2943 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2945 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2946 framecnt_t len = ar->fade_out()->back()->when;
2947 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2948 framepos_t new_length = len + diff;
2949 i->anchored_fade_length = min (ar->length(), new_length);
2950 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2951 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2959 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2961 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2962 i->view->move_contents (frame_delta);
2968 switch (_operation) {
2970 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2973 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2976 // show_verbose_cursor_time (frame_delta);
2982 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2984 if (movement_occurred) {
2985 motion (event, false);
2987 if (_operation == StartTrim) {
2988 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2990 /* This must happen before the region's StatefulDiffCommand is created, as it may
2991 `correct' (ahem) the region's _start from being negative to being zero. It
2992 needs to be zero in the undo record.
2994 i->view->trim_front_ending ();
2996 if (_preserve_fade_anchor) {
2997 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2999 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3000 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3001 ar->set_fade_in_length(i->anchored_fade_length);
3002 ar->set_fade_in_active(true);
3005 if (_jump_position_when_done) {
3006 i->view->region()->set_position (i->initial_position);
3009 } else if (_operation == EndTrim) {
3010 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3011 if (_preserve_fade_anchor) {
3012 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3014 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3015 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3016 ar->set_fade_out_length(i->anchored_fade_length);
3017 ar->set_fade_out_active(true);
3020 if (_jump_position_when_done) {
3021 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3026 if (!_views.empty()) {
3027 if (_operation == StartTrim) {
3028 _editor->maybe_locate_with_edit_preroll(
3029 _views.begin()->view->region()->position());
3031 if (_operation == EndTrim) {
3032 _editor->maybe_locate_with_edit_preroll(
3033 _views.begin()->view->region()->position() +
3034 _views.begin()->view->region()->length());
3038 if (!_editor->selection->selected (_primary)) {
3039 _primary->thaw_after_trim ();
3042 set<boost::shared_ptr<Playlist> > diffed_playlists;
3044 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3045 i->view->thaw_after_trim ();
3046 i->view->enable_display (true);
3048 /* Trimming one region may affect others on the playlist, so we need
3049 to get undo Commands from the whole playlist rather than just the
3050 region. Use diffed_playlists to make sure we don't diff a given
3051 playlist more than once.
3053 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3054 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3055 vector<Command*> cmds;
3057 _editor->session()->add_commands (cmds);
3058 diffed_playlists.insert (p);
3063 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3067 _editor->motion_frozen_playlists.clear ();
3068 _editor->commit_reversible_command();
3071 /* no mouse movement */
3072 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3073 _editor->point_trim (event, adjusted_current_frame (event));
3077 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3078 i->view->region()->resume_property_changes ();
3083 TrimDrag::aborted (bool movement_occurred)
3085 /* Our motion method is changing model state, so use the Undo system
3086 to cancel. Perhaps not ideal, as this will leave an Undo point
3087 behind which may be slightly odd from the user's point of view.
3091 finished (&ev, true);
3093 if (movement_occurred) {
3094 _editor->session()->undo (1);
3097 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3098 i->view->region()->resume_property_changes ();
3103 TrimDrag::setup_pointer_frame_offset ()
3105 list<DraggingView>::iterator i = _views.begin ();
3106 while (i != _views.end() && i->view != _primary) {
3110 if (i == _views.end()) {
3114 switch (_operation) {
3116 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3119 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3126 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3129 , _old_snap_type (e->snap_type())
3130 , _old_snap_mode (e->snap_mode())
3133 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3134 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3136 _real_section = &_marker->meter();
3141 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3143 Drag::start_grab (event, cursor);
3144 show_verbose_cursor_time (adjusted_current_frame(event));
3148 MeterMarkerDrag::setup_pointer_frame_offset ()
3150 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3154 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3157 // create a dummy marker to catch events, then hide it.
3160 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3162 _marker = new MeterMarker (
3164 *_editor->meter_group,
3165 UIConfiguration::instance().color ("meter marker"),
3167 *new MeterSection (_marker->meter())
3170 /* use the new marker for the grab */
3171 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3174 TempoMap& map (_editor->session()->tempo_map());
3175 /* get current state */
3176 before_state = &map.get_state();
3179 _editor->begin_reversible_command (_("move meter mark"));
3181 _editor->begin_reversible_command (_("copy meter mark"));
3183 Timecode::BBT_Time bbt = _real_section->bbt();
3185 /* we can't add a meter where one currently exists */
3186 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3191 const double beat = map.beat_at_bbt (bbt);
3192 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3193 , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style());
3194 if (!_real_section) {
3200 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3201 if (_real_section->position_lock_style() != AudioTime) {
3202 _editor->set_snap_to (SnapToBar);
3203 _editor->set_snap_mode (SnapNormal);
3207 framepos_t pf = adjusted_current_frame (event);
3209 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3210 /* never snap to music for audio locked */
3211 pf = adjusted_current_frame (event, false);
3214 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3216 /* fake marker meeds to stay under the mouse, unlike the real one. */
3217 _marker->set_position (adjusted_current_frame (event, false));
3219 show_verbose_cursor_time (_real_section->frame());
3223 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3225 if (!movement_occurred) {
3226 if (was_double_click()) {
3227 _editor->edit_meter_marker (*_marker);
3232 /* reinstate old snap setting */
3233 _editor->set_snap_to (_old_snap_type);
3234 _editor->set_snap_mode (_old_snap_mode);
3236 TempoMap& map (_editor->session()->tempo_map());
3238 XMLNode &after = map.get_state();
3239 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3240 _editor->commit_reversible_command ();
3242 // delete the dummy marker we used for visual representation while moving.
3243 // a new visual marker will show up automatically.
3248 MeterMarkerDrag::aborted (bool moved)
3250 _marker->set_position (_marker->meter().frame ());
3252 /* reinstate old snap setting */
3253 _editor->set_snap_to (_old_snap_type);
3254 _editor->set_snap_mode (_old_snap_mode);
3256 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3257 // delete the dummy marker we used for visual representation while moving.
3258 // a new visual marker will show up automatically.
3263 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3268 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3270 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3271 _real_section = &_marker->tempo();
3272 _movable = _real_section->movable();
3277 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3279 Drag::start_grab (event, cursor);
3280 if (!_real_section->active()) {
3281 show_verbose_cursor_text (_("inactive"));
3283 show_verbose_cursor_time (adjusted_current_frame (event));
3288 TempoMarkerDrag::setup_pointer_frame_offset ()
3290 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3294 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3296 if (!_real_section->active()) {
3302 // mvc drag - create a dummy marker to catch events, hide it.
3305 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3307 TempoSection section (_marker->tempo());
3309 _marker = new TempoMarker (
3311 *_editor->tempo_group,
3312 UIConfiguration::instance().color ("tempo marker"),
3314 *new TempoSection (_marker->tempo())
3317 /* use the new marker for the grab */
3318 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3321 TempoMap& map (_editor->session()->tempo_map());
3322 /* get current state */
3323 before_state = &map.get_state();
3326 _editor->begin_reversible_command (_("move tempo mark"));
3329 const Tempo tempo (_marker->tempo());
3330 const framepos_t frame = adjusted_current_frame (event) + 1;
3331 const TempoSection::Type type = _real_section->type();
3333 _editor->begin_reversible_command (_("copy tempo mark"));
3335 if (_real_section->position_lock_style() == MusicTime) {
3336 _real_section = map.add_tempo (tempo, map.pulse_at_frame (frame), 0, type, MusicTime);
3338 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3341 if (!_real_section) {
3349 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3350 /* use vertical movement to alter tempo .. should be log */
3351 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3354 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3356 show_verbose_cursor_text (strs.str());
3358 } else if (_movable && !_real_section->locked_to_meter()) {
3361 if (_editor->snap_musical()) {
3362 /* we can't snap to a grid that we are about to move.
3363 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3365 pf = adjusted_current_frame (event, false);
3367 pf = adjusted_current_frame (event);
3370 TempoMap& map (_editor->session()->tempo_map());
3372 /* snap to beat is 1, snap to bar is -1 (sorry) */
3373 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3375 map.gui_move_tempo (_real_section, pf, sub_num);
3377 show_verbose_cursor_time (_real_section->frame());
3379 _marker->set_position (adjusted_current_frame (event, false));
3383 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3385 if (!_real_section->active()) {
3388 if (!movement_occurred) {
3389 if (was_double_click()) {
3390 _editor->edit_tempo_marker (*_marker);
3395 TempoMap& map (_editor->session()->tempo_map());
3397 XMLNode &after = map.get_state();
3398 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3399 _editor->commit_reversible_command ();
3401 // delete the dummy marker we used for visual representation while moving.
3402 // a new visual marker will show up automatically.
3407 TempoMarkerDrag::aborted (bool moved)
3409 _marker->set_position (_marker->tempo().frame());
3411 TempoMap& map (_editor->session()->tempo_map());
3412 map.set_state (*before_state, Stateful::current_state_version);
3413 // delete the dummy (hidden) marker we used for events while moving.
3418 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3424 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3429 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3431 Drag::start_grab (event, cursor);
3432 TempoMap& map (_editor->session()->tempo_map());
3433 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3436 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3437 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3438 show_verbose_cursor_text (sstr.str());
3439 finished (event, false);
3443 BBTRulerDrag::setup_pointer_frame_offset ()
3445 TempoMap& map (_editor->session()->tempo_map());
3446 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3447 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3450 if (divisions > 0) {
3451 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3453 /* while it makes some sense for the user to determine the division to 'grab',
3454 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3455 and the result over steep tempo curves. Use sixteenths.
3457 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3460 _pulse = map.pulse_at_beat (beat);
3462 _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3467 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3469 TempoMap& map (_editor->session()->tempo_map());
3472 /* get current state */
3473 before_state = &map.get_state();
3474 _editor->begin_reversible_command (_("dilate tempo"));
3479 if (_editor->snap_musical()) {
3480 pf = adjusted_current_frame (event, false);
3482 pf = adjusted_current_frame (event);
3485 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3486 /* adjust previous tempo to match pointer frame */
3487 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3490 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3491 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3492 show_verbose_cursor_text (sstr.str());
3496 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3498 if (!movement_occurred) {
3502 TempoMap& map (_editor->session()->tempo_map());
3504 XMLNode &after = map.get_state();
3505 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3506 _editor->commit_reversible_command ();
3510 BBTRulerDrag::aborted (bool moved)
3513 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3518 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3519 : Drag (e, &c.track_canvas_item(), false)
3524 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3527 /** Do all the things we do when dragging the playhead to make it look as though
3528 * we have located, without actually doing the locate (because that would cause
3529 * the diskstream buffers to be refilled, which is too slow).
3532 CursorDrag::fake_locate (framepos_t t)
3534 if (_editor->session () == 0) {
3538 _editor->playhead_cursor->set_position (t);
3540 Session* s = _editor->session ();
3541 if (s->timecode_transmission_suspended ()) {
3542 framepos_t const f = _editor->playhead_cursor->current_frame ();
3543 /* This is asynchronous so it will be sent "now"
3545 s->send_mmc_locate (f);
3546 /* These are synchronous and will be sent during the next
3549 s->queue_full_time_code ();
3550 s->queue_song_position_pointer ();
3553 show_verbose_cursor_time (t);
3554 _editor->UpdateAllTransportClocks (t);
3558 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3560 Drag::start_grab (event, c);
3561 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3563 _grab_zoom = _editor->samples_per_pixel;
3565 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3567 _editor->snap_to_with_modifier (where, event);
3569 _editor->_dragging_playhead = true;
3571 Session* s = _editor->session ();
3573 /* grab the track canvas item as well */
3575 _cursor.track_canvas_item().grab();
3578 if (_was_rolling && _stop) {
3582 if (s->is_auditioning()) {
3583 s->cancel_audition ();
3587 if (AudioEngine::instance()->connected()) {
3589 /* do this only if we're the engine is connected
3590 * because otherwise this request will never be
3591 * serviced and we'll busy wait forever. likewise,
3592 * notice if we are disconnected while waiting for the
3593 * request to be serviced.
3596 s->request_suspend_timecode_transmission ();
3597 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3598 /* twiddle our thumbs */
3603 fake_locate (where - snap_delta (event->button.state));
3607 CursorDrag::motion (GdkEvent* event, bool)
3609 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3610 _editor->snap_to_with_modifier (where, event);
3611 if (where != last_pointer_frame()) {
3612 fake_locate (where - snap_delta (event->button.state));
3617 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3619 _editor->_dragging_playhead = false;
3621 _cursor.track_canvas_item().ungrab();
3623 if (!movement_occurred && _stop) {
3627 motion (event, false);
3629 Session* s = _editor->session ();
3631 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3632 _editor->_pending_locate_request = true;
3633 s->request_resume_timecode_transmission ();
3638 CursorDrag::aborted (bool)
3640 _cursor.track_canvas_item().ungrab();
3642 if (_editor->_dragging_playhead) {
3643 _editor->session()->request_resume_timecode_transmission ();
3644 _editor->_dragging_playhead = false;
3647 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3650 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3651 : RegionDrag (e, i, p, v)
3653 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3657 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3659 Drag::start_grab (event, cursor);
3661 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3662 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3663 setup_snap_delta (r->position ());
3665 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3669 FadeInDrag::setup_pointer_frame_offset ()
3671 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3672 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3673 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3677 FadeInDrag::motion (GdkEvent* event, bool)
3679 framecnt_t fade_length;
3681 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3682 _editor->snap_to_with_modifier (pos, event);
3683 pos -= snap_delta (event->button.state);
3685 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3687 if (pos < (region->position() + 64)) {
3688 fade_length = 64; // this should be a minimum defined somewhere
3689 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3690 fade_length = region->length() - region->fade_out()->back()->when - 1;
3692 fade_length = pos - region->position();
3695 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3697 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3703 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3706 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3710 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3712 if (!movement_occurred) {
3716 framecnt_t fade_length;
3717 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3718 _editor->snap_to_with_modifier (pos, event);
3719 pos -= snap_delta (event->button.state);
3721 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3723 if (pos < (region->position() + 64)) {
3724 fade_length = 64; // this should be a minimum defined somewhere
3725 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3726 fade_length = region->length() - region->fade_out()->back()->when - 1;
3728 fade_length = pos - region->position();
3731 bool in_command = false;
3733 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3735 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3741 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3742 XMLNode &before = alist->get_state();
3744 tmp->audio_region()->set_fade_in_length (fade_length);
3745 tmp->audio_region()->set_fade_in_active (true);
3748 _editor->begin_reversible_command (_("change fade in length"));
3751 XMLNode &after = alist->get_state();
3752 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3756 _editor->commit_reversible_command ();
3761 FadeInDrag::aborted (bool)
3763 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3764 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3770 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3774 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3775 : RegionDrag (e, i, p, v)
3777 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3781 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3783 Drag::start_grab (event, cursor);
3785 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3786 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3787 setup_snap_delta (r->last_frame ());
3789 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3793 FadeOutDrag::setup_pointer_frame_offset ()
3795 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3796 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3797 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3801 FadeOutDrag::motion (GdkEvent* event, bool)
3803 framecnt_t fade_length;
3805 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3806 _editor->snap_to_with_modifier (pos, event);
3807 pos -= snap_delta (event->button.state);
3809 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3811 if (pos > (region->last_frame() - 64)) {
3812 fade_length = 64; // this should really be a minimum fade defined somewhere
3813 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3814 fade_length = region->length() - region->fade_in()->back()->when - 1;
3816 fade_length = region->last_frame() - pos;
3819 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3821 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3827 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3830 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3834 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3836 if (!movement_occurred) {
3840 framecnt_t fade_length;
3842 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3843 _editor->snap_to_with_modifier (pos, event);
3844 pos -= snap_delta (event->button.state);
3846 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3848 if (pos > (region->last_frame() - 64)) {
3849 fade_length = 64; // this should really be a minimum fade defined somewhere
3850 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3851 fade_length = region->length() - region->fade_in()->back()->when - 1;
3853 fade_length = region->last_frame() - pos;
3856 bool in_command = false;
3858 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3860 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3866 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3867 XMLNode &before = alist->get_state();
3869 tmp->audio_region()->set_fade_out_length (fade_length);
3870 tmp->audio_region()->set_fade_out_active (true);
3873 _editor->begin_reversible_command (_("change fade out length"));
3876 XMLNode &after = alist->get_state();
3877 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3881 _editor->commit_reversible_command ();
3886 FadeOutDrag::aborted (bool)
3888 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3889 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3895 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3899 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3901 , _selection_changed (false)
3903 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3904 Gtk::Window* toplevel = _editor->current_toplevel();
3905 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3909 _points.push_back (ArdourCanvas::Duple (0, 0));
3911 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3914 MarkerDrag::~MarkerDrag ()
3916 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3921 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3923 location = new Location (*l);
3924 markers.push_back (m);
3929 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3931 Drag::start_grab (event, cursor);
3935 Location *location = _editor->find_location_from_marker (_marker, is_start);
3936 _editor->_dragging_edit_point = true;
3938 update_item (location);
3940 // _drag_line->show();
3941 // _line->raise_to_top();
3944 show_verbose_cursor_time (location->start());
3946 show_verbose_cursor_time (location->end());
3948 setup_snap_delta (is_start ? location->start() : location->end());
3950 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3953 case Selection::Toggle:
3954 /* we toggle on the button release */
3956 case Selection::Set:
3957 if (!_editor->selection->selected (_marker)) {
3958 _editor->selection->set (_marker);
3959 _selection_changed = true;
3962 case Selection::Extend:
3964 Locations::LocationList ll;
3965 list<ArdourMarker*> to_add;
3967 _editor->selection->markers.range (s, e);
3968 s = min (_marker->position(), s);
3969 e = max (_marker->position(), e);
3972 if (e < max_framepos) {
3975 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3976 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3977 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3980 to_add.push_back (lm->start);
3983 to_add.push_back (lm->end);
3987 if (!to_add.empty()) {
3988 _editor->selection->add (to_add);
3989 _selection_changed = true;
3993 case Selection::Add:
3994 _editor->selection->add (_marker);
3995 _selection_changed = true;
4000 /* Set up copies for us to manipulate during the drag
4003 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4005 Location* l = _editor->find_location_from_marker (*i, is_start);
4012 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4014 /* range: check that the other end of the range isn't
4017 CopiedLocationInfo::iterator x;
4018 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4019 if (*(*x).location == *l) {
4023 if (x == _copied_locations.end()) {
4024 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4026 (*x).markers.push_back (*i);
4027 (*x).move_both = true;
4035 MarkerDrag::setup_pointer_frame_offset ()
4038 Location *location = _editor->find_location_from_marker (_marker, is_start);
4039 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4043 MarkerDrag::motion (GdkEvent* event, bool)
4045 framecnt_t f_delta = 0;
4047 bool move_both = false;
4048 Location *real_location;
4049 Location *copy_location = 0;
4050 framecnt_t const sd = snap_delta (event->button.state);
4052 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4053 framepos_t next = newframe;
4055 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4059 CopiedLocationInfo::iterator x;
4061 /* find the marker we're dragging, and compute the delta */
4063 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4065 copy_location = (*x).location;
4067 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4069 /* this marker is represented by this
4070 * CopiedLocationMarkerInfo
4073 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4078 if (real_location->is_mark()) {
4079 f_delta = newframe - copy_location->start();
4083 switch (_marker->type()) {
4084 case ArdourMarker::SessionStart:
4085 case ArdourMarker::RangeStart:
4086 case ArdourMarker::LoopStart:
4087 case ArdourMarker::PunchIn:
4088 f_delta = newframe - copy_location->start();
4091 case ArdourMarker::SessionEnd:
4092 case ArdourMarker::RangeEnd:
4093 case ArdourMarker::LoopEnd:
4094 case ArdourMarker::PunchOut:
4095 f_delta = newframe - copy_location->end();
4098 /* what kind of marker is this ? */
4107 if (x == _copied_locations.end()) {
4108 /* hmm, impossible - we didn't find the dragged marker */
4112 /* now move them all */
4114 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4116 copy_location = x->location;
4118 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4122 if (real_location->locked()) {
4126 if (copy_location->is_mark()) {
4130 copy_location->set_start (copy_location->start() + f_delta);
4134 framepos_t new_start = copy_location->start() + f_delta;
4135 framepos_t new_end = copy_location->end() + f_delta;
4137 if (is_start) { // start-of-range marker
4139 if (move_both || (*x).move_both) {
4140 copy_location->set_start (new_start);
4141 copy_location->set_end (new_end);
4142 } else if (new_start < copy_location->end()) {
4143 copy_location->set_start (new_start);
4144 } else if (newframe > 0) {
4145 //_editor->snap_to (next, RoundUpAlways, true);
4146 copy_location->set_end (next);
4147 copy_location->set_start (newframe);
4150 } else { // end marker
4152 if (move_both || (*x).move_both) {
4153 copy_location->set_end (new_end);
4154 copy_location->set_start (new_start);
4155 } else if (new_end > copy_location->start()) {
4156 copy_location->set_end (new_end);
4157 } else if (newframe > 0) {
4158 //_editor->snap_to (next, RoundDownAlways, true);
4159 copy_location->set_start (next);
4160 copy_location->set_end (newframe);
4165 update_item (copy_location);
4167 /* now lookup the actual GUI items used to display this
4168 * location and move them to wherever the copy of the location
4169 * is now. This means that the logic in ARDOUR::Location is
4170 * still enforced, even though we are not (yet) modifying
4171 * the real Location itself.
4174 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4177 lm->set_position (copy_location->start(), copy_location->end());
4182 assert (!_copied_locations.empty());
4184 show_verbose_cursor_time (newframe);
4188 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4190 if (!movement_occurred) {
4192 if (was_double_click()) {
4193 _editor->rename_marker (_marker);
4197 /* just a click, do nothing but finish
4198 off the selection process
4201 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4203 case Selection::Set:
4204 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4205 _editor->selection->set (_marker);
4206 _selection_changed = true;
4210 case Selection::Toggle:
4211 /* we toggle on the button release, click only */
4212 _editor->selection->toggle (_marker);
4213 _selection_changed = true;
4217 case Selection::Extend:
4218 case Selection::Add:
4222 if (_selection_changed) {
4223 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4224 _editor->commit_reversible_selection_op();
4230 _editor->_dragging_edit_point = false;
4232 XMLNode &before = _editor->session()->locations()->get_state();
4233 bool in_command = false;
4235 MarkerSelection::iterator i;
4236 CopiedLocationInfo::iterator x;
4239 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4240 x != _copied_locations.end() && i != _editor->selection->markers.end();
4243 Location * location = _editor->find_location_from_marker (*i, is_start);
4247 if (location->locked()) {
4251 _editor->begin_reversible_command ( _("move marker") );
4254 if (location->is_mark()) {
4255 location->set_start (((*x).location)->start());
4257 location->set (((*x).location)->start(), ((*x).location)->end());
4260 if (location->is_session_range()) {
4261 _editor->session()->set_end_is_free (false);
4267 XMLNode &after = _editor->session()->locations()->get_state();
4268 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4269 _editor->commit_reversible_command ();
4274 MarkerDrag::aborted (bool movement_occurred)
4276 if (!movement_occurred) {
4280 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4282 /* move all markers to their original location */
4285 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4288 Location * location = _editor->find_location_from_marker (*m, is_start);
4291 (*m)->set_position (is_start ? location->start() : location->end());
4298 MarkerDrag::update_item (Location*)
4303 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4305 , _fixed_grab_x (0.0)
4306 , _fixed_grab_y (0.0)
4307 , _cumulative_x_drag (0.0)
4308 , _cumulative_y_drag (0.0)
4312 if (_zero_gain_fraction < 0.0) {
4313 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4316 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4318 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4324 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4326 Drag::start_grab (event, _editor->cursors()->fader);
4328 // start the grab at the center of the control point so
4329 // the point doesn't 'jump' to the mouse after the first drag
4330 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4331 _fixed_grab_y = _point->get_y();
4333 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4334 setup_snap_delta (pos);
4336 float const fraction = 1 - (_point->get_y() / _point->line().height());
4337 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4339 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4341 if (!_point->can_slide ()) {
4342 _x_constrained = true;
4347 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4349 double dx = _drags->current_pointer_x() - last_pointer_x();
4350 double dy = current_pointer_y() - last_pointer_y();
4351 bool need_snap = true;
4353 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4359 /* coordinate in pixels relative to the start of the region (for region-based automation)
4360 or track (for track-based automation) */
4361 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4362 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4364 // calculate zero crossing point. back off by .01 to stay on the
4365 // positive side of zero
4366 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4368 if (_x_constrained) {
4371 if (_y_constrained) {
4375 _cumulative_x_drag = cx - _fixed_grab_x;
4376 _cumulative_y_drag = cy - _fixed_grab_y;
4380 cy = min ((double) _point->line().height(), cy);
4382 // make sure we hit zero when passing through
4383 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4387 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4388 if (!_x_constrained && need_snap) {
4389 _editor->snap_to_with_modifier (cx_frames, event);
4392 cx_frames -= snap_delta (event->button.state);
4393 cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset());
4395 float const fraction = 1.0 - (cy / _point->line().height());
4398 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4399 _editor->begin_reversible_command (_("automation event move"));
4400 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4402 pair<double, float> result;
4403 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4405 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4409 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4411 if (!movement_occurred) {
4414 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4415 _editor->reset_point_selection ();
4419 _point->line().end_drag (_pushing, _final_index);
4420 _editor->commit_reversible_command ();
4425 ControlPointDrag::aborted (bool)
4427 _point->line().reset ();
4431 ControlPointDrag::active (Editing::MouseMode m)
4433 if (m == Editing::MouseDraw) {
4434 /* always active in mouse draw */
4438 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4439 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4442 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4445 , _fixed_grab_x (0.0)
4446 , _fixed_grab_y (0.0)
4447 , _cumulative_y_drag (0)
4451 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4455 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4457 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4460 _item = &_line->grab_item ();
4462 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4463 origin, and ditto for y.
4466 double mx = event->button.x;
4467 double my = event->button.y;
4469 _line->grab_item().canvas_to_item (mx, my);
4471 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4473 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4474 /* no adjacent points */
4478 Drag::start_grab (event, _editor->cursors()->fader);
4480 /* store grab start in item frame */
4481 double const bx = _line->nth (_before)->get_x();
4482 double const ax = _line->nth (_after)->get_x();
4483 double const click_ratio = (ax - mx) / (ax - bx);
4485 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4490 double fraction = 1.0 - (cy / _line->height());
4492 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4496 LineDrag::motion (GdkEvent* event, bool first_move)
4498 double dy = current_pointer_y() - last_pointer_y();
4500 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4504 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4506 _cumulative_y_drag = cy - _fixed_grab_y;
4509 cy = min ((double) _line->height(), cy);
4511 double const fraction = 1.0 - (cy / _line->height());
4515 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4517 _editor->begin_reversible_command (_("automation range move"));
4518 _line->start_drag_line (_before, _after, initial_fraction);
4521 /* we are ignoring x position for this drag, so we can just pass in anything */
4522 pair<double, float> result;
4524 result = _line->drag_motion (0, fraction, true, false, ignored);
4525 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4529 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4531 if (movement_occurred) {
4532 motion (event, false);
4533 _line->end_drag (false, 0);
4534 _editor->commit_reversible_command ();
4536 /* add a new control point on the line */
4538 AutomationTimeAxisView* atv;
4540 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4541 framepos_t where = grab_frame ();
4544 double cy = _fixed_grab_y;
4546 _line->grab_item().item_to_canvas (cx, cy);
4548 atv->add_automation_event (event, where, cy, false);
4549 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4550 AudioRegionView* arv;
4552 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4553 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4560 LineDrag::aborted (bool)
4565 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4569 _region_view_grab_x (0.0),
4570 _cumulative_x_drag (0),
4574 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4578 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4580 Drag::start_grab (event);
4582 _line = reinterpret_cast<Line*> (_item);
4585 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4587 double cx = event->button.x;
4588 double cy = event->button.y;
4590 _item->parent()->canvas_to_item (cx, cy);
4592 /* store grab start in parent frame */
4593 _region_view_grab_x = cx;
4595 _before = *(float*) _item->get_data ("position");
4597 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4599 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4603 FeatureLineDrag::motion (GdkEvent*, bool)
4605 double dx = _drags->current_pointer_x() - last_pointer_x();
4607 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4609 _cumulative_x_drag += dx;
4611 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4620 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4622 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4624 float *pos = new float;
4627 _line->set_data ("position", pos);
4633 FeatureLineDrag::finished (GdkEvent*, bool)
4635 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4636 _arv->update_transient(_before, _before);
4640 FeatureLineDrag::aborted (bool)
4645 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4647 , _vertical_only (false)
4649 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4653 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4655 Drag::start_grab (event);
4656 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4660 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4667 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4669 framepos_t grab = grab_frame ();
4670 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4671 _editor->snap_to_with_modifier (grab, event);
4673 grab = raw_grab_frame ();
4676 /* base start and end on initial click position */
4686 if (current_pointer_y() < grab_y()) {
4687 y1 = current_pointer_y();
4690 y2 = current_pointer_y();
4694 if (start != end || y1 != y2) {
4696 double x1 = _editor->sample_to_pixel (start);
4697 double x2 = _editor->sample_to_pixel (end);
4698 const double min_dimension = 2.0;
4700 if (_vertical_only) {
4701 /* fixed 10 pixel width */
4705 x2 = min (x1 - min_dimension, x2);
4707 x2 = max (x1 + min_dimension, x2);
4712 y2 = min (y1 - min_dimension, y2);
4714 y2 = max (y1 + min_dimension, y2);
4717 /* translate rect into item space and set */
4719 ArdourCanvas::Rect r (x1, y1, x2, y2);
4721 /* this drag is a _trackview_only == true drag, so the y1 and
4722 * y2 (computed using current_pointer_y() and grab_y()) will be
4723 * relative to the top of the trackview group). The
4724 * rubberband rect has the same parent/scroll offset as the
4725 * the trackview group, so we can use the "r" rect directly
4726 * to set the shape of the rubberband.
4729 _editor->rubberband_rect->set (r);
4730 _editor->rubberband_rect->show();
4731 _editor->rubberband_rect->raise_to_top();
4733 show_verbose_cursor_time (pf);
4735 do_select_things (event, true);
4740 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4744 framepos_t grab = grab_frame ();
4745 framepos_t lpf = last_pointer_frame ();
4747 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4748 grab = raw_grab_frame ();
4749 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4763 if (current_pointer_y() < grab_y()) {
4764 y1 = current_pointer_y();
4767 y2 = current_pointer_y();
4771 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4775 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4777 if (movement_occurred) {
4779 motion (event, false);
4780 do_select_things (event, false);
4786 bool do_deselect = true;
4787 MidiTimeAxisView* mtv;
4789 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4791 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4792 /* nothing selected */
4793 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4794 do_deselect = false;
4798 /* do not deselect if Primary or Tertiary (toggle-select or
4799 * extend-select are pressed.
4802 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4803 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4810 _editor->rubberband_rect->hide();
4814 RubberbandSelectDrag::aborted (bool)
4816 _editor->rubberband_rect->hide ();
4819 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4820 : RegionDrag (e, i, p, v)
4822 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4826 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4828 Drag::start_grab (event, cursor);
4830 _editor->get_selection().add (_primary);
4832 framepos_t where = _primary->region()->position();
4833 setup_snap_delta (where);
4835 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4839 TimeFXDrag::motion (GdkEvent* event, bool)
4841 RegionView* rv = _primary;
4842 StreamView* cv = rv->get_time_axis_view().view ();
4844 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4845 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4846 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4847 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4848 _editor->snap_to_with_modifier (pf, event);
4849 pf -= snap_delta (event->button.state);
4851 if (pf > rv->region()->position()) {
4852 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4855 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4859 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4861 /* this may have been a single click, no drag. We still want the dialog
4862 to show up in that case, so that the user can manually edit the
4863 parameters for the timestretch.
4866 float fraction = 1.0;
4868 if (movement_occurred) {
4870 motion (event, false);
4872 _primary->get_time_axis_view().hide_timestretch ();
4874 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4876 if (adjusted_frame_pos < _primary->region()->position()) {
4877 /* backwards drag of the left edge - not usable */
4881 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4883 fraction = (double) newlen / (double) _primary->region()->length();
4885 #ifndef USE_RUBBERBAND
4886 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4887 if (_primary->region()->data_type() == DataType::AUDIO) {
4888 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4893 if (!_editor->get_selection().regions.empty()) {
4894 /* primary will already be included in the selection, and edit
4895 group shared editing will propagate selection across
4896 equivalent regions, so just use the current region
4900 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4901 error << _("An error occurred while executing time stretch operation") << endmsg;
4907 TimeFXDrag::aborted (bool)
4909 _primary->get_time_axis_view().hide_timestretch ();
4912 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4915 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4919 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4921 Drag::start_grab (event);
4925 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4927 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4931 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4933 if (movement_occurred && _editor->session()) {
4934 /* make sure we stop */
4935 _editor->session()->request_transport_speed (0.0);
4940 ScrubDrag::aborted (bool)
4945 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4949 , _time_selection_at_start (!_editor->get_selection().time.empty())
4951 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4953 if (_time_selection_at_start) {
4954 start_at_start = _editor->get_selection().time.start();
4955 end_at_start = _editor->get_selection().time.end_frame();
4960 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4962 if (_editor->session() == 0) {
4966 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4968 switch (_operation) {
4969 case CreateSelection:
4970 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4975 cursor = _editor->cursors()->selector;
4976 Drag::start_grab (event, cursor);
4979 case SelectionStartTrim:
4980 if (_editor->clicked_axisview) {
4981 _editor->clicked_axisview->order_selection_trims (_item, true);
4983 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4986 case SelectionEndTrim:
4987 if (_editor->clicked_axisview) {
4988 _editor->clicked_axisview->order_selection_trims (_item, false);
4990 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4994 Drag::start_grab (event, cursor);
4997 case SelectionExtend:
4998 Drag::start_grab (event, cursor);
5002 if (_operation == SelectionMove) {
5003 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5005 show_verbose_cursor_time (adjusted_current_frame (event));
5010 SelectionDrag::setup_pointer_frame_offset ()
5012 switch (_operation) {
5013 case CreateSelection:
5014 _pointer_frame_offset = 0;
5017 case SelectionStartTrim:
5019 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5022 case SelectionEndTrim:
5023 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5026 case SelectionExtend:
5032 SelectionDrag::motion (GdkEvent* event, bool first_move)
5034 framepos_t start = 0;
5036 framecnt_t length = 0;
5037 framecnt_t distance = 0;
5039 framepos_t const pending_position = adjusted_current_frame (event);
5041 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5045 switch (_operation) {
5046 case CreateSelection:
5048 framepos_t grab = grab_frame ();
5051 grab = adjusted_current_frame (event, false);
5052 if (grab < pending_position) {
5053 _editor->snap_to (grab, RoundDownMaybe);
5055 _editor->snap_to (grab, RoundUpMaybe);
5059 if (pending_position < grab) {
5060 start = pending_position;
5063 end = pending_position;
5067 /* first drag: Either add to the selection
5068 or create a new selection
5075 /* adding to the selection */
5076 _editor->set_selected_track_as_side_effect (Selection::Add);
5077 _editor->clicked_selection = _editor->selection->add (start, end);
5084 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5085 _editor->set_selected_track_as_side_effect (Selection::Set);
5088 _editor->clicked_selection = _editor->selection->set (start, end);
5092 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5093 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5094 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5096 _editor->selection->add (atest);
5100 /* select all tracks within the rectangle that we've marked out so far */
5101 TrackViewList new_selection;
5102 TrackViewList& all_tracks (_editor->track_views);
5104 ArdourCanvas::Coord const top = grab_y();
5105 ArdourCanvas::Coord const bottom = current_pointer_y();
5107 if (top >= 0 && bottom >= 0) {
5109 //first, find the tracks that are covered in the y range selection
5110 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5111 if ((*i)->covered_by_y_range (top, bottom)) {
5112 new_selection.push_back (*i);
5116 //now find any tracks that are GROUPED with the tracks we selected
5117 TrackViewList grouped_add = new_selection;
5118 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5119 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5120 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5121 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5122 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5123 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5124 grouped_add.push_back (*j);
5129 //now compare our list with the current selection, and add or remove as necessary
5130 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5131 TrackViewList tracks_to_add;
5132 TrackViewList tracks_to_remove;
5133 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5134 if ( !_editor->selection->tracks.contains ( *i ) )
5135 tracks_to_add.push_back ( *i );
5136 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5137 if ( !grouped_add.contains ( *i ) )
5138 tracks_to_remove.push_back ( *i );
5139 _editor->selection->add(tracks_to_add);
5140 _editor->selection->remove(tracks_to_remove);
5146 case SelectionStartTrim:
5148 end = _editor->selection->time[_editor->clicked_selection].end;
5150 if (pending_position > end) {
5153 start = pending_position;
5157 case SelectionEndTrim:
5159 start = _editor->selection->time[_editor->clicked_selection].start;
5161 if (pending_position < start) {
5164 end = pending_position;
5171 start = _editor->selection->time[_editor->clicked_selection].start;
5172 end = _editor->selection->time[_editor->clicked_selection].end;
5174 length = end - start;
5175 distance = pending_position - start;
5176 start = pending_position;
5177 _editor->snap_to (start);
5179 end = start + length;
5183 case SelectionExtend:
5188 switch (_operation) {
5190 if (_time_selection_at_start) {
5191 _editor->selection->move_time (distance);
5195 _editor->selection->replace (_editor->clicked_selection, start, end);
5199 if (_operation == SelectionMove) {
5200 show_verbose_cursor_time(start);
5202 show_verbose_cursor_time(pending_position);
5207 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5209 Session* s = _editor->session();
5211 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5212 if (movement_occurred) {
5213 motion (event, false);
5214 /* XXX this is not object-oriented programming at all. ick */
5215 if (_editor->selection->time.consolidate()) {
5216 _editor->selection->TimeChanged ();
5219 /* XXX what if its a music time selection? */
5221 if (s->get_play_range() && s->transport_rolling()) {
5222 s->request_play_range (&_editor->selection->time, true);
5223 } else if (!s->config.get_external_sync()) {
5224 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5225 if (_operation == SelectionEndTrim)
5226 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5228 s->request_locate (_editor->get_selection().time.start());
5232 if (_editor->get_selection().time.length() != 0) {
5233 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5235 s->clear_range_selection ();
5240 /* just a click, no pointer movement.
5243 if (_operation == SelectionExtend) {
5244 if (_time_selection_at_start) {
5245 framepos_t pos = adjusted_current_frame (event, false);
5246 framepos_t start = min (pos, start_at_start);
5247 framepos_t end = max (pos, end_at_start);
5248 _editor->selection->set (start, end);
5251 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5252 if (_editor->clicked_selection) {
5253 _editor->selection->remove (_editor->clicked_selection);
5256 if (!_editor->clicked_selection) {
5257 _editor->selection->clear_time();
5262 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5263 _editor->selection->set (_editor->clicked_axisview);
5266 if (s && s->get_play_range () && s->transport_rolling()) {
5267 s->request_stop (false, false);
5272 _editor->stop_canvas_autoscroll ();
5273 _editor->clicked_selection = 0;
5274 _editor->commit_reversible_selection_op ();
5278 SelectionDrag::aborted (bool)
5283 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5284 : Drag (e, i, false),
5288 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5290 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5291 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5292 physical_screen_height (_editor->current_toplevel()->get_window())));
5293 _drag_rect->hide ();
5295 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5296 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5299 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5301 /* normal canvas items will be cleaned up when their parent group is deleted. But
5302 this item is created as the child of a long-lived parent group, and so we
5303 need to explicitly delete it.
5309 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5311 if (_editor->session() == 0) {
5315 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5317 if (!_editor->temp_location) {
5318 _editor->temp_location = new Location (*_editor->session());
5321 switch (_operation) {
5322 case CreateSkipMarker:
5323 case CreateRangeMarker:
5324 case CreateTransportMarker:
5325 case CreateCDMarker:
5327 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5332 cursor = _editor->cursors()->selector;
5336 Drag::start_grab (event, cursor);
5338 show_verbose_cursor_time (adjusted_current_frame (event));
5342 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5344 framepos_t start = 0;
5346 ArdourCanvas::Rectangle *crect;
5348 switch (_operation) {
5349 case CreateSkipMarker:
5350 crect = _editor->range_bar_drag_rect;
5352 case CreateRangeMarker:
5353 crect = _editor->range_bar_drag_rect;
5355 case CreateTransportMarker:
5356 crect = _editor->transport_bar_drag_rect;
5358 case CreateCDMarker:
5359 crect = _editor->cd_marker_bar_drag_rect;
5362 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5367 framepos_t const pf = adjusted_current_frame (event);
5369 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5370 framepos_t grab = grab_frame ();
5371 _editor->snap_to (grab);
5373 if (pf < grab_frame()) {
5381 /* first drag: Either add to the selection
5382 or create a new selection.
5387 _editor->temp_location->set (start, end);
5391 update_item (_editor->temp_location);
5393 //_drag_rect->raise_to_top();
5399 _editor->temp_location->set (start, end);
5401 double x1 = _editor->sample_to_pixel (start);
5402 double x2 = _editor->sample_to_pixel (end);
5406 update_item (_editor->temp_location);
5409 show_verbose_cursor_time (pf);
5414 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5416 Location * newloc = 0;
5420 if (movement_occurred) {
5421 motion (event, false);
5424 switch (_operation) {
5425 case CreateSkipMarker:
5426 case CreateRangeMarker:
5427 case CreateCDMarker:
5429 XMLNode &before = _editor->session()->locations()->get_state();
5430 if (_operation == CreateSkipMarker) {
5431 _editor->begin_reversible_command (_("new skip marker"));
5432 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5433 flags = Location::IsRangeMarker | Location::IsSkip;
5434 _editor->range_bar_drag_rect->hide();
5435 } else if (_operation == CreateCDMarker) {
5436 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5437 _editor->begin_reversible_command (_("new CD marker"));
5438 flags = Location::IsRangeMarker | Location::IsCDMarker;
5439 _editor->cd_marker_bar_drag_rect->hide();
5441 _editor->begin_reversible_command (_("new skip marker"));
5442 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5443 flags = Location::IsRangeMarker;
5444 _editor->range_bar_drag_rect->hide();
5446 newloc = new Location (
5447 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5450 _editor->session()->locations()->add (newloc, true);
5451 XMLNode &after = _editor->session()->locations()->get_state();
5452 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5453 _editor->commit_reversible_command ();
5457 case CreateTransportMarker:
5458 // popup menu to pick loop or punch
5459 _editor->new_transport_marker_context_menu (&event->button, _item);
5465 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5467 if (_operation == CreateTransportMarker) {
5469 /* didn't drag, so just locate */
5471 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5473 } else if (_operation == CreateCDMarker) {
5475 /* didn't drag, but mark is already created so do
5478 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5483 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5485 if (end == max_framepos) {
5486 end = _editor->session()->current_end_frame ();
5489 if (start == max_framepos) {
5490 start = _editor->session()->current_start_frame ();
5493 switch (_editor->mouse_mode) {
5495 /* find the two markers on either side and then make the selection from it */
5496 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5500 /* find the two markers on either side of the click and make the range out of it */
5501 _editor->selection->set (start, end);
5510 _editor->stop_canvas_autoscroll ();
5514 RangeMarkerBarDrag::aborted (bool movement_occurred)
5516 if (movement_occurred) {
5517 _drag_rect->hide ();
5522 RangeMarkerBarDrag::update_item (Location* location)
5524 double const x1 = _editor->sample_to_pixel (location->start());
5525 double const x2 = _editor->sample_to_pixel (location->end());
5527 _drag_rect->set_x0 (x1);
5528 _drag_rect->set_x1 (x2);
5531 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5533 , _cumulative_dx (0)
5534 , _cumulative_dy (0)
5535 , _was_selected (false)
5537 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5539 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5541 _region = &_primary->region_view ();
5542 _note_height = _region->midi_stream_view()->note_height ();
5546 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5548 Drag::start_grab (event);
5549 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5551 if (!(_was_selected = _primary->selected())) {
5553 /* tertiary-click means extend selection - we'll do that on button release,
5554 so don't add it here, because otherwise we make it hard to figure
5555 out the "extend-to" range.
5558 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5561 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5564 _region->note_selected (_primary, true);
5566 _editor->get_selection().clear_points();
5567 _region->unique_select (_primary);
5573 /** @return Current total drag x change in frames */
5575 NoteDrag::total_dx (const guint state) const
5577 if (_x_constrained) {
5580 TempoMap& map (_editor->session()->tempo_map());
5583 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5585 /* primary note time */
5586 double const quarter_note_start = (_region->region()->pulse() * 4.0) - _region->midi_region()->start_beats();
5587 frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5589 /* new time of the primary note in session frames */
5590 frameoffset_t st = n + dx + snap_delta (state);
5592 framepos_t const rp = _region->region()->position ();
5594 /* prevent the note being dragged earlier than the region's position */
5597 /* possibly snap and return corresponding delta */
5601 if (ArdourKeyboard::indicates_snap (state)) {
5602 if (_editor->snap_mode () != SnapOff) {
5606 if (_editor->snap_mode () == SnapOff) {
5608 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5609 if (ArdourKeyboard::indicates_snap_delta (state)) {
5617 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5618 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5620 ret = st - n - snap_delta (state);
5625 /** @return Current total drag y change in note number */
5627 NoteDrag::total_dy () const
5629 if (_y_constrained) {
5633 MidiStreamView* msv = _region->midi_stream_view ();
5634 double const y = _region->midi_view()->y_position ();
5635 /* new current note */
5636 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5638 n = max (msv->lowest_note(), n);
5639 n = min (msv->highest_note(), n);
5640 /* and work out delta */
5641 return n - msv->y_to_note (grab_y() - y);
5645 NoteDrag::motion (GdkEvent * event, bool)
5647 /* Total change in x and y since the start of the drag */
5648 frameoffset_t const dx = total_dx (event->button.state);
5649 int8_t const dy = total_dy ();
5651 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5652 double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5653 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5656 _cumulative_dx += tdx;
5657 _cumulative_dy += tdy;
5659 int8_t note_delta = total_dy();
5662 _region->move_selection (tdx, tdy, note_delta);
5664 /* the new note value may be the same as the old one, but we
5665 * don't know what that means because the selection may have
5666 * involved more than one note and we might be doing something
5667 * odd with them. so show the note value anyway, always.
5670 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5672 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5678 NoteDrag::finished (GdkEvent* ev, bool moved)
5681 /* no motion - select note */
5683 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5684 _editor->current_mouse_mode() == Editing::MouseDraw) {
5686 bool changed = false;
5688 if (_was_selected) {
5689 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5691 _region->note_deselected (_primary);
5694 _editor->get_selection().clear_points();
5695 _region->unique_select (_primary);
5699 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5700 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5702 if (!extend && !add && _region->selection_size() > 1) {
5703 _editor->get_selection().clear_points();
5704 _region->unique_select (_primary);
5706 } else if (extend) {
5707 _region->note_selected (_primary, true, true);
5710 /* it was added during button press */
5717 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5718 _editor->commit_reversible_selection_op();
5722 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5727 NoteDrag::aborted (bool)
5732 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5733 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5734 : Drag (editor, atv->base_item ())
5736 , _y_origin (atv->y_position())
5737 , _nothing_to_drag (false)
5739 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5740 setup (atv->lines ());
5743 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5744 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5745 : Drag (editor, rv->get_canvas_group ())
5747 , _y_origin (rv->get_time_axis_view().y_position())
5748 , _nothing_to_drag (false)
5751 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5753 list<boost::shared_ptr<AutomationLine> > lines;
5755 AudioRegionView* audio_view;
5756 AutomationRegionView* automation_view;
5757 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5758 lines.push_back (audio_view->get_gain_line ());
5759 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5760 lines.push_back (automation_view->line ());
5763 error << _("Automation range drag created for invalid region type") << endmsg;
5769 /** @param lines AutomationLines to drag.
5770 * @param offset Offset from the session start to the points in the AutomationLines.
5773 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5775 /* find the lines that overlap the ranges being dragged */
5776 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5777 while (i != lines.end ()) {
5778 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5781 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5783 /* check this range against all the AudioRanges that we are using */
5784 list<AudioRange>::const_iterator k = _ranges.begin ();
5785 while (k != _ranges.end()) {
5786 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5792 /* add it to our list if it overlaps at all */
5793 if (k != _ranges.end()) {
5798 _lines.push_back (n);
5804 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5808 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5810 return 1.0 - ((global_y - _y_origin) / line->height());
5814 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5816 const double v = list->eval(x);
5817 return _integral ? rint(v) : v;
5821 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5823 Drag::start_grab (event, cursor);
5825 /* Get line states before we start changing things */
5826 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5827 i->state = &i->line->get_state ();
5828 i->original_fraction = y_fraction (i->line, current_pointer_y());
5831 if (_ranges.empty()) {
5833 /* No selected time ranges: drag all points */
5834 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5835 uint32_t const N = i->line->npoints ();
5836 for (uint32_t j = 0; j < N; ++j) {
5837 i->points.push_back (i->line->nth (j));
5843 if (_nothing_to_drag) {
5849 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5851 if (_nothing_to_drag && !first_move) {
5856 _editor->begin_reversible_command (_("automation range move"));
5858 if (!_ranges.empty()) {
5860 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5862 framecnt_t const half = (i->start + i->end) / 2;
5864 /* find the line that this audio range starts in */
5865 list<Line>::iterator j = _lines.begin();
5866 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5870 if (j != _lines.end()) {
5871 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5873 /* j is the line that this audio range starts in; fade into it;
5874 64 samples length plucked out of thin air.
5877 framepos_t a = i->start + 64;
5882 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5883 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5885 XMLNode &before = the_list->get_state();
5886 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5887 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5889 if (add_p || add_q) {
5890 _editor->session()->add_command (
5891 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5895 /* same thing for the end */
5898 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5902 if (j != _lines.end()) {
5903 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5905 /* j is the line that this audio range starts in; fade out of it;
5906 64 samples length plucked out of thin air.
5909 framepos_t b = i->end - 64;
5914 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5915 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5917 XMLNode &before = the_list->get_state();
5918 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5919 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5921 if (add_p || add_q) {
5922 _editor->session()->add_command (
5923 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5928 _nothing_to_drag = true;
5930 /* Find all the points that should be dragged and put them in the relevant
5931 points lists in the Line structs.
5934 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5936 uint32_t const N = i->line->npoints ();
5937 for (uint32_t j = 0; j < N; ++j) {
5939 /* here's a control point on this line */
5940 ControlPoint* p = i->line->nth (j);
5941 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5943 /* see if it's inside a range */
5944 list<AudioRange>::const_iterator k = _ranges.begin ();
5945 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5949 if (k != _ranges.end()) {
5950 /* dragging this point */
5951 _nothing_to_drag = false;
5952 i->points.push_back (p);
5958 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5959 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5963 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5964 float const f = y_fraction (l->line, current_pointer_y());
5965 /* we are ignoring x position for this drag, so we can just pass in anything */
5966 pair<double, float> result;
5968 result = l->line->drag_motion (0, f, true, false, ignored);
5969 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5974 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5976 if (_nothing_to_drag || !motion_occurred) {
5980 motion (event, false);
5981 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5982 i->line->end_drag (false, 0);
5985 _editor->commit_reversible_command ();
5989 AutomationRangeDrag::aborted (bool)
5991 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5996 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5998 , initial_time_axis_view (itav)
6000 /* note that time_axis_view may be null if the regionview was created
6001 * as part of a copy operation.
6003 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6004 layer = v->region()->layer ();
6005 initial_y = v->get_canvas_group()->position().y;
6006 initial_playlist = v->region()->playlist ();
6007 initial_position = v->region()->position ();
6008 initial_end = v->region()->position () + v->region()->length ();
6011 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6012 : Drag (e, i->canvas_item ())
6015 , _cumulative_dx (0)
6017 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6018 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6023 PatchChangeDrag::motion (GdkEvent* ev, bool)
6025 framepos_t f = adjusted_current_frame (ev);
6026 boost::shared_ptr<Region> r = _region_view->region ();
6027 f = max (f, r->position ());
6028 f = min (f, r->last_frame ());
6030 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6031 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6032 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6033 _cumulative_dx = dxu;
6037 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6039 if (!movement_occurred) {
6040 if (was_double_click()) {
6041 _region_view->edit_patch_change (_patch_change);
6046 boost::shared_ptr<Region> r (_region_view->region ());
6047 framepos_t f = adjusted_current_frame (ev);
6048 f = max (f, r->position ());
6049 f = min (f, r->last_frame ());
6051 _region_view->move_patch_change (
6053 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6058 PatchChangeDrag::aborted (bool)
6060 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6064 PatchChangeDrag::setup_pointer_frame_offset ()
6066 boost::shared_ptr<Region> region = _region_view->region ();
6067 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6070 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6071 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6078 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6080 _region_view->update_drag_selection (
6082 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6086 MidiRubberbandSelectDrag::deselect_things ()
6091 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6092 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6095 _vertical_only = true;
6099 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6101 double const y = _region_view->midi_view()->y_position ();
6103 y1 = max (0.0, y1 - y);
6104 y2 = max (0.0, y2 - y);
6106 _region_view->update_vertical_drag_selection (
6109 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6114 MidiVerticalSelectDrag::deselect_things ()
6119 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6120 : RubberbandSelectDrag (e, i)
6126 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6128 if (drag_in_progress) {
6129 /* We just want to select things at the end of the drag, not during it */
6133 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6135 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6137 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6139 _editor->commit_reversible_selection_op ();
6143 EditorRubberbandSelectDrag::deselect_things ()
6145 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6147 _editor->selection->clear_tracks();
6148 _editor->selection->clear_regions();
6149 _editor->selection->clear_points ();
6150 _editor->selection->clear_lines ();
6151 _editor->selection->clear_midi_notes ();
6153 _editor->commit_reversible_selection_op();
6156 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6161 _note[0] = _note[1] = 0;
6164 NoteCreateDrag::~NoteCreateDrag ()
6170 NoteCreateDrag::grid_frames (framepos_t t) const
6173 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6175 grid_beats = Evoral::Beats(1);
6178 return _region_view->region_beats_to_region_frames (grid_beats);
6182 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6184 Drag::start_grab (event, cursor);
6186 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6187 TempoMap& map (_editor->session()->tempo_map());
6189 const framepos_t pf = _drags->current_pointer_frame ();
6190 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6192 double eqaf = map.exact_qn_at_frame (pf, divisions);
6194 if (divisions != 0) {
6195 bool success = false;
6196 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf);
6198 grid_beats = Evoral::Beats(1);
6201 const double qaf = map.quarter_note_at_frame (pf);
6203 /* Hack so that we always snap to the note that we are over, instead of snapping
6204 to the next one if we're more than halfway through the one we're over.
6207 const double rem = eqaf - qaf;
6209 eqaf -= grid_beats.to_double();
6213 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6214 _note[1] = _note[0];
6216 MidiStreamView* sv = _region_view->midi_stream_view ();
6217 double const x = _editor->sample_to_pixel (_note[0]);
6218 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6220 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6221 _drag_rect->set_outline_all ();
6222 _drag_rect->set_outline_color (0xffffff99);
6223 _drag_rect->set_fill_color (0xffffff66);
6227 NoteCreateDrag::motion (GdkEvent* event, bool)
6229 TempoMap& map (_editor->session()->tempo_map());
6230 const framepos_t pf = _drags->current_pointer_frame ();
6231 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6232 double eqaf = map.exact_qn_at_frame (pf, divisions);
6234 if (divisions != 0) {
6235 bool success = false;
6236 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf);
6238 grid_beats = Evoral::Beats(1);
6241 const double qaf = map.quarter_note_at_frame (pf);
6242 /* Hack so that we always snap to the note that we are over, instead of snapping
6243 to the next one if we're more than halfway through the one we're over.
6246 const double rem = eqaf - qaf;
6248 eqaf -= grid_beats.to_double();
6251 eqaf += grid_beats.to_double();
6253 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6255 double const x0 = _editor->sample_to_pixel (_note[0]);
6256 double const x1 = _editor->sample_to_pixel (_note[1]);
6257 _drag_rect->set_x0 (std::min(x0, x1));
6258 _drag_rect->set_x1 (std::max(x0, x1));
6262 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6264 if (!had_movement) {
6268 framepos_t const start = min (_note[0], _note[1]);
6269 framepos_t const start_sess_rel = start + _region_view->region()->position();
6270 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6272 framecnt_t const g = grid_frames (start);
6273 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6275 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6279 TempoMap& map (_editor->session()->tempo_map());
6280 const double qn_length = map.quarter_note_at_frame (start_sess_rel + length) - map.quarter_note_at_frame (start_sess_rel);
6282 Evoral::Beats qn_length_beats = max (one_tick, Evoral::Beats (qn_length));
6283 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6287 NoteCreateDrag::y_to_region (double y) const
6290 _region_view->get_canvas_group()->canvas_to_item (x, y);
6295 NoteCreateDrag::aborted (bool)
6300 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6305 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6309 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6311 Drag::start_grab (event, cursor);
6315 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6321 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6324 distance = _drags->current_pointer_x() - grab_x();
6325 len = ar->fade_in()->back()->when;
6327 distance = grab_x() - _drags->current_pointer_x();
6328 len = ar->fade_out()->back()->when;
6331 /* how long should it be ? */
6333 new_length = len + _editor->pixel_to_sample (distance);
6335 /* now check with the region that this is legal */
6337 new_length = ar->verify_xfade_bounds (new_length, start);
6340 arv->reset_fade_in_shape_width (ar, new_length);
6342 arv->reset_fade_out_shape_width (ar, new_length);
6347 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6353 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6356 distance = _drags->current_pointer_x() - grab_x();
6357 len = ar->fade_in()->back()->when;
6359 distance = grab_x() - _drags->current_pointer_x();
6360 len = ar->fade_out()->back()->when;
6363 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6365 _editor->begin_reversible_command ("xfade trim");
6366 ar->playlist()->clear_owned_changes ();
6369 ar->set_fade_in_length (new_length);
6371 ar->set_fade_out_length (new_length);
6374 /* Adjusting the xfade may affect other regions in the playlist, so we need
6375 to get undo Commands from the whole playlist rather than just the
6379 vector<Command*> cmds;
6380 ar->playlist()->rdiff (cmds);
6381 _editor->session()->add_commands (cmds);
6382 _editor->commit_reversible_command ();
6387 CrossfadeEdgeDrag::aborted (bool)
6390 // arv->redraw_start_xfade ();
6392 // arv->redraw_end_xfade ();
6396 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6397 : Drag (e, item, true)
6398 , line (new EditorCursor (*e))
6400 line->set_position (pos);
6404 RegionCutDrag::~RegionCutDrag ()
6410 RegionCutDrag::motion (GdkEvent*, bool)
6412 framepos_t where = _drags->current_pointer_frame();
6413 _editor->snap_to (where);
6415 line->set_position (where);
6419 RegionCutDrag::finished (GdkEvent* event, bool)
6421 _editor->get_track_canvas()->canvas()->re_enter();
6423 framepos_t pos = _drags->current_pointer_frame();
6427 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6433 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state));
6437 RegionCutDrag::aborted (bool)