2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
70 using namespace ARDOUR;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
81 DragManager::DragManager (Editor* e)
84 , _current_pointer_frame (0)
88 DragManager::~DragManager ()
93 /** Call abort for each active drag */
99 cerr << "Aborting drag\n";
101 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
106 if (!_drags.empty ()) {
107 _editor->set_follow_playhead (_old_follow_playhead, false);
116 DragManager::add (Drag* d)
118 d->set_manager (this);
119 _drags.push_back (d);
123 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
125 d->set_manager (this);
126 _drags.push_back (d);
131 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
133 /* Prevent follow playhead during the drag to be nice to the user */
134 _old_follow_playhead = _editor->follow_playhead ();
135 _editor->set_follow_playhead (false);
137 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
139 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
140 (*i)->start_grab (e, c);
144 /** Call end_grab for each active drag.
145 * @return true if any drag reported movement having occurred.
148 DragManager::end_grab (GdkEvent* e)
153 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
154 bool const t = (*i)->end_grab (e);
165 _editor->set_follow_playhead (_old_follow_playhead, false);
171 DragManager::mark_double_click ()
173 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
174 (*i)->set_double_click (true);
179 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
183 /* calling this implies that we expect the event to have canvas
186 * Can we guarantee that this is true?
189 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
191 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
192 bool const t = (*i)->motion_handler (e, from_autoscroll);
193 /* run all handlers; return true if at least one of them
194 returns true (indicating that the event has been handled).
206 DragManager::have_item (ArdourCanvas::Item* i) const
208 list<Drag*>::const_iterator j = _drags.begin ();
209 while (j != _drags.end() && (*j)->item () != i) {
213 return j != _drags.end ();
216 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
219 , _pointer_frame_offset (0)
220 , _trackview_only (trackview_only)
221 , _move_threshold_passed (false)
222 , _was_double_click (false)
223 , _raw_grab_frame (0)
225 , _last_pointer_frame (0)
231 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
244 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
246 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
248 if (Keyboard::is_button2_event (&event->button)) {
249 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
250 _y_constrained = true;
251 _x_constrained = false;
253 _y_constrained = false;
254 _x_constrained = true;
257 _x_constrained = false;
258 _y_constrained = false;
261 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
262 setup_pointer_frame_offset ();
263 _grab_frame = adjusted_frame (_raw_grab_frame, event);
264 _last_pointer_frame = _grab_frame;
265 _last_pointer_x = _grab_x;
267 if (_trackview_only) {
268 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
271 _last_pointer_y = _grab_y;
276 /* CAIROCANVAS need a variant here that passes *cursor */
278 _cursor_ctx = CursorContext::create(*_editor, cursor);
281 if (_editor->session() && _editor->session()->transport_rolling()) {
284 _was_rolling = false;
287 switch (_editor->snap_type()) {
288 case SnapToRegionStart:
289 case SnapToRegionEnd:
290 case SnapToRegionSync:
291 case SnapToRegionBoundary:
292 _editor->build_region_boundary_cache ();
299 /** Call to end a drag `successfully'. Ungrabs item and calls
300 * subclass' finished() method.
302 * @param event GDK event, or 0.
303 * @return true if some movement occurred, otherwise false.
306 Drag::end_grab (GdkEvent* event)
308 _editor->stop_canvas_autoscroll ();
312 finished (event, _move_threshold_passed);
314 _editor->verbose_cursor()->hide ();
317 return _move_threshold_passed;
321 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
325 if (f > _pointer_frame_offset) {
326 pos = f - _pointer_frame_offset;
330 _editor->snap_to_with_modifier (pos, event);
337 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
339 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
343 Drag::current_pointer_x() const
345 return _drags->current_pointer_x ();
349 Drag::current_pointer_y () const
351 if (!_trackview_only) {
352 return _drags->current_pointer_y ();
355 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
359 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
361 /* check to see if we have moved in any way that matters since the last motion event */
362 if (_move_threshold_passed &&
363 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
364 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
368 pair<framecnt_t, int> const threshold = move_threshold ();
370 bool const old_move_threshold_passed = _move_threshold_passed;
372 if (!from_autoscroll && !_move_threshold_passed) {
374 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
375 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
377 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
380 if (active (_editor->mouse_mode) && _move_threshold_passed) {
382 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
383 if (!from_autoscroll) {
384 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
387 if (!_editor->autoscroll_active() || from_autoscroll) {
388 motion (event, _move_threshold_passed != old_move_threshold_passed);
390 _last_pointer_x = _drags->current_pointer_x ();
391 _last_pointer_y = current_pointer_y ();
392 _last_pointer_frame = adjusted_current_frame (event);
402 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
410 aborted (_move_threshold_passed);
412 _editor->stop_canvas_autoscroll ();
413 _editor->verbose_cursor()->hide ();
417 Drag::show_verbose_cursor_time (framepos_t frame)
419 _editor->verbose_cursor()->set_time (frame);
420 _editor->verbose_cursor()->show ();
424 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
426 _editor->verbose_cursor()->set_duration (start, end);
427 _editor->verbose_cursor()->show ();
431 Drag::show_verbose_cursor_text (string const & text)
433 _editor->verbose_cursor()->set (text);
434 _editor->verbose_cursor()->show ();
437 boost::shared_ptr<Region>
438 Drag::add_midi_region (MidiTimeAxisView* view)
440 if (_editor->session()) {
441 const TempoMap& map (_editor->session()->tempo_map());
442 framecnt_t pos = grab_frame();
443 const Meter& m = map.meter_at (pos);
444 /* not that the frame rate used here can be affected by pull up/down which
447 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
448 return view->add_region (grab_frame(), len, true);
451 return boost::shared_ptr<Region>();
454 struct EditorOrderTimeAxisViewSorter {
455 bool operator() (TimeAxisView* a, TimeAxisView* b) {
456 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
457 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
459 return ra->route()->order_key () < rb->route()->order_key ();
463 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
467 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
469 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
470 as some of the regions we are dragging may be on such tracks.
473 TrackViewList track_views = _editor->track_views;
474 track_views.sort (EditorOrderTimeAxisViewSorter ());
476 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
477 _time_axis_views.push_back (*i);
479 TimeAxisView::Children children_list = (*i)->get_child_list ();
480 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
481 _time_axis_views.push_back (j->get());
485 /* the list of views can be empty at this point if this is a region list-insert drag
488 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
489 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
492 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
496 RegionDrag::region_going_away (RegionView* v)
498 list<DraggingView>::iterator i = _views.begin ();
499 while (i != _views.end() && i->view != v) {
503 if (i != _views.end()) {
508 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
509 * or -1 if it is not found.
512 RegionDrag::find_time_axis_view (TimeAxisView* t) const
515 int const N = _time_axis_views.size ();
516 while (i < N && _time_axis_views[i] != t) {
527 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
528 : RegionDrag (e, i, p, v)
531 , _last_pointer_time_axis_view (0)
532 , _last_pointer_layer (0)
534 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
538 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
540 Drag::start_grab (event, cursor);
542 show_verbose_cursor_time (_last_frame_position);
544 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
546 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
547 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
552 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
554 /* compute the amount of pointer motion in frames, and where
555 the region would be if we moved it by that much.
557 *pending_region_position = adjusted_current_frame (event);
559 framepos_t sync_frame;
560 framecnt_t sync_offset;
563 sync_offset = _primary->region()->sync_offset (sync_dir);
565 /* we don't handle a sync point that lies before zero.
567 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
569 sync_frame = *pending_region_position + (sync_dir*sync_offset);
571 _editor->snap_to_with_modifier (sync_frame, event);
573 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
576 *pending_region_position = _last_frame_position;
579 if (*pending_region_position > max_framepos - _primary->region()->length()) {
580 *pending_region_position = _last_frame_position;
585 /* in locked edit mode, reverse the usual meaning of _x_constrained */
586 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
588 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
590 /* x movement since last time (in pixels) */
591 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
593 /* total x movement */
594 framecnt_t total_dx = *pending_region_position;
595 if (regions_came_from_canvas()) {
596 total_dx = total_dx - grab_frame ();
599 /* check that no regions have gone off the start of the session */
600 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
601 if ((i->view->region()->position() + total_dx) < 0) {
603 *pending_region_position = _last_frame_position;
614 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
616 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
617 int const n = i->time_axis_view + delta_track;
618 if (n < 0 || n >= int (_time_axis_views.size())) {
619 /* off the top or bottom track */
623 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
624 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
625 /* not a track, or the wrong type */
629 double const l = i->layer + delta_layer;
631 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
632 mode to allow the user to place a region below another on layer 0.
634 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
635 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
636 If it has, the layers will be munged later anyway, so it's ok.
642 /* all regions being dragged are ok with this change */
647 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
649 double delta_layer = 0;
650 int delta_time_axis_view = 0;
652 assert (!_views.empty ());
654 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
656 /* Find the TimeAxisView that the pointer is now over */
657 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
658 TimeAxisView* tv = r.first;
660 if (tv && tv->view()) {
661 double layer = r.second;
663 if (first_move && tv->view()->layer_display() == Stacked) {
664 tv->view()->set_layer_display (Expanded);
667 /* Here's the current pointer position in terms of time axis view and layer */
668 int const current_pointer_time_axis_view = find_time_axis_view (tv);
669 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
671 /* Work out the change in y */
673 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
674 delta_layer = current_pointer_layer - _last_pointer_layer;
677 /* Work out the change in x */
678 framepos_t pending_region_position;
679 double const x_delta = compute_x_delta (event, &pending_region_position);
680 _last_frame_position = pending_region_position;
682 /* Verify change in y */
683 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
684 /* this y movement is not allowed, so do no y movement this time */
685 delta_time_axis_view = 0;
689 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
690 /* haven't reached next snap point, and we're not switching
691 trackviews nor layers. nothing to do.
696 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
698 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
700 RegionView* rv = i->view;
702 if (rv->region()->locked() || rv->region()->video_locked()) {
709 /* reparent the regionview into a group above all
713 ArdourCanvas::Item* rvg = rv->get_canvas_group();
714 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
715 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
716 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
717 /* move the item so that it continues to appear at the
718 same location now that its parent has changed.
720 rvg->move (rv_canvas_offset - dmg_canvas_offset);
723 /* If we have moved tracks, we'll fudge the layer delta so that the
724 region gets moved back onto layer 0 on its new track; this avoids
725 confusion when dragging regions from non-zero layers onto different
728 double this_delta_layer = delta_layer;
729 if (delta_time_axis_view != 0) {
730 this_delta_layer = - i->layer;
737 if (i->time_axis_view >= 0) {
738 track_index = i->time_axis_view + delta_time_axis_view;
740 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
743 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
747 /* The TimeAxisView that this region is now over */
748 TimeAxisView* current_tv = _time_axis_views[track_index];
750 /* Ensure it is moved from stacked -> expanded if appropriate */
751 if (current_tv->view()->layer_display() == Stacked) {
752 current_tv->view()->set_layer_display (Expanded);
755 /* We're only allowed to go -ve in layer on Expanded views */
756 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
757 this_delta_layer = - i->layer;
761 rv->set_height (current_tv->view()->child_height ());
763 /* Update show/hidden status as the region view may have come from a hidden track,
764 or have moved to one.
766 if (current_tv->hidden ()) {
767 rv->get_canvas_group()->hide ();
769 rv->get_canvas_group()->show ();
772 /* Update the DraggingView */
773 i->time_axis_view = track_index;
774 i->layer += this_delta_layer;
777 _editor->mouse_brush_insert_region (rv, pending_region_position);
781 /* Get the y coordinate of the top of the track that this region is now over */
782 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
784 /* And adjust for the layer that it should be on */
785 StreamView* cv = current_tv->view ();
786 switch (cv->layer_display ()) {
790 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
793 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
797 /* need to get the parent of the regionview
798 * canvas group and get its position in
799 * equivalent coordinate space as the trackview
800 * we are now dragging over.
803 /* Now move the region view */
804 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
808 /* Only move the region into the empty dropzone at the bottom if the pointer
812 if (current_pointer_y() >= 0) {
814 Coord last_track_bottom_edge;
815 if (!_time_axis_views.empty()) {
816 TimeAxisView* last = _time_axis_views.back();
817 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
819 last_track_bottom_edge = 0;
822 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
823 i->time_axis_view = -1;
827 } /* foreach region */
829 _total_x_delta += x_delta;
831 if (x_delta != 0 && !_brushing) {
832 show_verbose_cursor_time (_last_frame_position);
835 _last_pointer_time_axis_view += delta_time_axis_view;
836 _last_pointer_layer += delta_layer;
840 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
842 if (_copy && first_move) {
844 if (_x_constrained) {
845 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
847 _editor->begin_reversible_command (Operations::region_copy);
850 /* duplicate the regionview(s) and region(s) */
852 list<DraggingView> new_regionviews;
854 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
856 RegionView* rv = i->view;
857 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
858 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
860 const boost::shared_ptr<const Region> original = rv->region();
861 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
862 region_copy->set_position (original->position());
866 boost::shared_ptr<AudioRegion> audioregion_copy
867 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
869 nrv = new AudioRegionView (*arv, audioregion_copy);
871 boost::shared_ptr<MidiRegion> midiregion_copy
872 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
873 nrv = new MidiRegionView (*mrv, midiregion_copy);
878 nrv->get_canvas_group()->show ();
879 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
881 /* swap _primary to the copy */
883 if (rv == _primary) {
887 /* ..and deselect the one we copied */
889 rv->set_selected (false);
892 if (!new_regionviews.empty()) {
894 /* reflect the fact that we are dragging the copies */
896 _views = new_regionviews;
898 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
901 } else if (!_copy && first_move) {
903 if (_x_constrained) {
904 _editor->begin_reversible_command (_("fixed time region drag"));
906 _editor->begin_reversible_command (Operations::region_drag);
910 RegionMotionDrag::motion (event, first_move);
914 RegionMotionDrag::finished (GdkEvent *, bool)
916 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
921 if ((*i)->view()->layer_display() == Expanded) {
922 (*i)->view()->set_layer_display (Stacked);
928 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
930 RegionMotionDrag::finished (ev, movement_occurred);
932 if (!movement_occurred) {
936 if (was_double_click() && !_views.empty()) {
937 DraggingView dv = _views.front();
938 dv.view->show_region_editor ();
945 /* reverse this here so that we have the correct logic to finalize
949 if (Config->get_edit_mode() == Lock) {
950 _x_constrained = !_x_constrained;
953 assert (!_views.empty ());
955 /* We might have hidden region views so that they weren't visible during the drag
956 (when they have been reparented). Now everything can be shown again, as region
957 views are back in their track parent groups.
959 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
960 i->view->get_canvas_group()->show ();
963 bool const changed_position = (_last_frame_position != _primary->region()->position());
964 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
965 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
985 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
989 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
991 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
996 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
997 list<boost::shared_ptr<AudioTrack> > audio_tracks;
998 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
999 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1001 rtav->set_height (original->current_height());
1005 ChanCount one_midi_port (DataType::MIDI, 1);
1006 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1007 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1008 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1010 rtav->set_height (original->current_height());
1015 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1021 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1023 RegionSelection new_views;
1024 PlaylistSet modified_playlists;
1025 RouteTimeAxisView* new_time_axis_view = 0;
1028 /* all changes were made during motion event handlers */
1030 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1034 _editor->commit_reversible_command ();
1038 /* insert the regions into their new playlists */
1039 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1041 RouteTimeAxisView* dest_rtv = 0;
1043 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1049 if (changed_position && !_x_constrained) {
1050 where = i->view->region()->position() - drag_delta;
1052 where = i->view->region()->position();
1055 if (i->time_axis_view < 0) {
1056 if (!new_time_axis_view) {
1057 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1059 dest_rtv = new_time_axis_view;
1061 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1064 if (dest_rtv != 0) {
1065 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1066 if (new_view != 0) {
1067 new_views.push_back (new_view);
1071 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1072 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1075 list<DraggingView>::const_iterator next = i;
1081 /* If we've created new regions either by copying or moving
1082 to a new track, we want to replace the old selection with the new ones
1085 if (new_views.size() > 0) {
1086 _editor->selection->set (new_views);
1089 /* write commands for the accumulated diffs for all our modified playlists */
1090 add_stateful_diff_commands_for_playlists (modified_playlists);
1092 _editor->commit_reversible_command ();
1096 RegionMoveDrag::finished_no_copy (
1097 bool const changed_position,
1098 bool const changed_tracks,
1099 framecnt_t const drag_delta
1102 RegionSelection new_views;
1103 PlaylistSet modified_playlists;
1104 PlaylistSet frozen_playlists;
1105 set<RouteTimeAxisView*> views_to_update;
1106 RouteTimeAxisView* new_time_axis_view = 0;
1109 /* all changes were made during motion event handlers */
1110 _editor->commit_reversible_command ();
1114 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1116 RegionView* rv = i->view;
1117 RouteTimeAxisView* dest_rtv = 0;
1119 if (rv->region()->locked() || rv->region()->video_locked()) {
1124 if (i->time_axis_view < 0) {
1125 if (!new_time_axis_view) {
1126 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1128 dest_rtv = new_time_axis_view;
1130 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1135 double const dest_layer = i->layer;
1137 views_to_update.insert (dest_rtv);
1141 if (changed_position && !_x_constrained) {
1142 where = rv->region()->position() - drag_delta;
1144 where = rv->region()->position();
1147 if (changed_tracks) {
1149 /* insert into new playlist */
1151 RegionView* new_view = insert_region_into_playlist (
1152 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1155 if (new_view == 0) {
1160 new_views.push_back (new_view);
1162 /* remove from old playlist */
1164 /* the region that used to be in the old playlist is not
1165 moved to the new one - we use a copy of it. as a result,
1166 any existing editor for the region should no longer be
1169 rv->hide_region_editor();
1172 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1176 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1178 /* this movement may result in a crossfade being modified, or a layering change,
1179 so we need to get undo data from the playlist as well as the region.
1182 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1184 playlist->clear_changes ();
1187 rv->region()->clear_changes ();
1190 motion on the same track. plonk the previously reparented region
1191 back to its original canvas group (its streamview).
1192 No need to do anything for copies as they are fake regions which will be deleted.
1195 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1196 rv->get_canvas_group()->set_y_position (i->initial_y);
1199 /* just change the model */
1200 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1201 playlist->set_layer (rv->region(), dest_layer);
1204 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1206 r = frozen_playlists.insert (playlist);
1209 playlist->freeze ();
1212 rv->region()->set_position (where);
1214 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1217 if (changed_tracks) {
1219 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1220 was selected in all of them, then removing it from a playlist will have removed all
1221 trace of it from _views (i.e. there were N regions selected, we removed 1,
1222 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1223 corresponding regionview, and _views is now empty).
1225 This could have invalidated any and all iterators into _views.
1227 The heuristic we use here is: if the region selection is empty, break out of the loop
1228 here. if the region selection is not empty, then restart the loop because we know that
1229 we must have removed at least the region(view) we've just been working on as well as any
1230 that we processed on previous iterations.
1232 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1233 we can just iterate.
1237 if (_views.empty()) {
1248 /* If we've created new regions either by copying or moving
1249 to a new track, we want to replace the old selection with the new ones
1252 if (new_views.size() > 0) {
1253 _editor->selection->set (new_views);
1256 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1260 /* write commands for the accumulated diffs for all our modified playlists */
1261 add_stateful_diff_commands_for_playlists (modified_playlists);
1263 _editor->commit_reversible_command ();
1265 /* We have futzed with the layering of canvas items on our streamviews.
1266 If any region changed layer, this will have resulted in the stream
1267 views being asked to set up their region views, and all will be well.
1268 If not, we might now have badly-ordered region views. Ask the StreamViews
1269 involved to sort themselves out, just in case.
1272 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1273 (*i)->view()->playlist_layered ((*i)->track ());
1277 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1278 * @param region Region to remove.
1279 * @param playlist playlist To remove from.
1280 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1281 * that clear_changes () is only called once per playlist.
1284 RegionMoveDrag::remove_region_from_playlist (
1285 boost::shared_ptr<Region> region,
1286 boost::shared_ptr<Playlist> playlist,
1287 PlaylistSet& modified_playlists
1290 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1293 playlist->clear_changes ();
1296 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1300 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1301 * clearing the playlist's diff history first if necessary.
1302 * @param region Region to insert.
1303 * @param dest_rtv Destination RouteTimeAxisView.
1304 * @param dest_layer Destination layer.
1305 * @param where Destination position.
1306 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1307 * that clear_changes () is only called once per playlist.
1308 * @return New RegionView, or 0 if no insert was performed.
1311 RegionMoveDrag::insert_region_into_playlist (
1312 boost::shared_ptr<Region> region,
1313 RouteTimeAxisView* dest_rtv,
1316 PlaylistSet& modified_playlists
1319 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1320 if (!dest_playlist) {
1324 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1325 _new_region_view = 0;
1326 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1328 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1329 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1331 dest_playlist->clear_changes ();
1334 dest_playlist->add_region (region, where);
1336 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1337 dest_playlist->set_layer (region, dest_layer);
1342 assert (_new_region_view);
1344 return _new_region_view;
1348 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1350 _new_region_view = rv;
1354 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1356 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1357 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1359 _editor->session()->add_command (c);
1368 RegionMoveDrag::aborted (bool movement_occurred)
1372 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1379 RegionMotionDrag::aborted (movement_occurred);
1384 RegionMotionDrag::aborted (bool)
1386 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1388 StreamView* sview = (*i)->view();
1391 if (sview->layer_display() == Expanded) {
1392 sview->set_layer_display (Stacked);
1397 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1398 RegionView* rv = i->view;
1399 TimeAxisView* tv = &(rv->get_time_axis_view ());
1400 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1402 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1403 rv->get_canvas_group()->set_y_position (0);
1405 rv->move (-_total_x_delta, 0);
1406 rv->set_height (rtv->view()->child_height ());
1410 /** @param b true to brush, otherwise false.
1411 * @param c true to make copies of the regions being moved, otherwise false.
1413 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1414 : RegionMotionDrag (e, i, p, v, b),
1417 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1420 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1421 if (rtv && rtv->is_track()) {
1422 speed = rtv->track()->speed ();
1425 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1429 RegionMoveDrag::setup_pointer_frame_offset ()
1431 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1434 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1435 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1437 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1439 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1440 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1442 _primary = v->view()->create_region_view (r, false, false);
1444 _primary->get_canvas_group()->show ();
1445 _primary->set_position (pos, 0);
1446 _views.push_back (DraggingView (_primary, this, v));
1448 _last_frame_position = pos;
1450 _item = _primary->get_canvas_group ();
1454 RegionInsertDrag::finished (GdkEvent *, bool)
1456 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1458 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1459 _primary->get_canvas_group()->set_y_position (0);
1461 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1463 _editor->begin_reversible_command (Operations::insert_region);
1464 playlist->clear_changes ();
1465 playlist->add_region (_primary->region (), _last_frame_position);
1467 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1468 if (Config->get_edit_mode() == Ripple) {
1469 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1472 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1473 _editor->commit_reversible_command ();
1481 RegionInsertDrag::aborted (bool)
1488 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1489 : RegionMoveDrag (e, i, p, v, false, false)
1491 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1494 struct RegionSelectionByPosition {
1495 bool operator() (RegionView*a, RegionView* b) {
1496 return a->region()->position () < b->region()->position();
1501 RegionSpliceDrag::motion (GdkEvent* event, bool)
1503 /* Which trackview is this ? */
1505 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1506 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1508 /* The region motion is only processed if the pointer is over
1512 if (!tv || !tv->is_track()) {
1513 /* To make sure we hide the verbose canvas cursor when the mouse is
1514 not held over an audio track.
1516 _editor->verbose_cursor()->hide ();
1519 _editor->verbose_cursor()->show ();
1524 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1530 RegionSelection copy;
1531 _editor->selection->regions.by_position(copy);
1533 framepos_t const pf = adjusted_current_frame (event);
1535 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1537 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1543 boost::shared_ptr<Playlist> playlist;
1545 if ((playlist = atv->playlist()) == 0) {
1549 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1554 if (pf < (*i)->region()->last_frame() + 1) {
1558 if (pf > (*i)->region()->first_frame()) {
1564 playlist->shuffle ((*i)->region(), dir);
1569 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1571 RegionMoveDrag::finished (event, movement_occurred);
1575 RegionSpliceDrag::aborted (bool)
1585 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1588 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1590 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1591 RegionSelection to_ripple;
1592 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1593 if ((*i)->position() >= where) {
1594 to_ripple.push_back (rtv->view()->find_view(*i));
1598 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1599 if (!exclude.contains (*i)) {
1600 // the selection has already been added to _views
1602 if (drag_in_progress) {
1603 // do the same things that RegionMotionDrag::motion does when
1604 // first_move is true, for the region views that we're adding
1605 // to _views this time
1608 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1609 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1610 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1611 rvg->reparent (_editor->_drag_motion_group);
1613 // we only need to move in the y direction
1614 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1619 _views.push_back (DraggingView (*i, this, tav));
1625 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1628 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1629 // we added all the regions after the selection
1631 std::list<DraggingView>::iterator to_erase = i++;
1632 if (!_editor->selection->regions.contains (to_erase->view)) {
1633 // restore the non-selected regions to their original playlist & positions,
1634 // and then ripple them back by the length of the regions that were dragged away
1635 // do the same things as RegionMotionDrag::aborted
1637 RegionView *rv = to_erase->view;
1638 TimeAxisView* tv = &(rv->get_time_axis_view ());
1639 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1642 // plonk them back onto their own track
1643 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1644 rv->get_canvas_group()->set_y_position (0);
1648 // move the underlying region to match the view
1649 rv->region()->set_position (rv->region()->position() + amount);
1651 // restore the view to match the underlying region's original position
1652 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1655 rv->set_height (rtv->view()->child_height ());
1656 _views.erase (to_erase);
1662 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1664 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1666 return allow_moves_across_tracks;
1674 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1675 : RegionMoveDrag (e, i, p, v, false, false)
1677 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1678 // compute length of selection
1679 RegionSelection selected_regions = _editor->selection->regions;
1680 selection_length = selected_regions.end_frame() - selected_regions.start();
1682 // we'll only allow dragging to another track in ripple mode if all the regions
1683 // being dragged start off on the same track
1684 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1687 exclude = new RegionList;
1688 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1689 exclude->push_back((*i)->region());
1692 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1693 RegionSelection copy;
1694 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1696 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1697 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1699 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1700 // find ripple start point on each applicable playlist
1701 RegionView *first_selected_on_this_track = NULL;
1702 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1703 if ((*i)->region()->playlist() == (*pi)) {
1704 // region is on this playlist - it's the first, because they're sorted
1705 first_selected_on_this_track = *i;
1709 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1710 add_all_after_to_views (
1711 &first_selected_on_this_track->get_time_axis_view(),
1712 first_selected_on_this_track->region()->position(),
1713 selected_regions, false);
1716 if (allow_moves_across_tracks) {
1717 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1725 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1727 /* Which trackview is this ? */
1729 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1730 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1732 /* The region motion is only processed if the pointer is over
1736 if (!tv || !tv->is_track()) {
1737 /* To make sure we hide the verbose canvas cursor when the mouse is
1738 not held over an audiotrack.
1740 _editor->verbose_cursor()->hide ();
1744 framepos_t where = adjusted_current_frame (event);
1745 assert (where >= 0);
1747 double delta = compute_x_delta (event, &after);
1749 framecnt_t amount = _editor->pixel_to_sample (delta);
1751 if (allow_moves_across_tracks) {
1752 // all the originally selected regions were on the same track
1754 framecnt_t adjust = 0;
1755 if (prev_tav && tv != prev_tav) {
1756 // dragged onto a different track
1757 // remove the unselected regions from _views, restore them to their original positions
1758 // and add the regions after the drop point on the new playlist to _views instead.
1759 // undo the effect of rippling the previous playlist, and include the effect of removing
1760 // the dragged region(s) from this track
1762 remove_unselected_from_views (prev_amount, false);
1763 // ripple previous playlist according to the regions that have been removed onto the new playlist
1764 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1767 // move just the selected regions
1768 RegionMoveDrag::motion(event, first_move);
1770 // ensure that the ripple operation on the new playlist inserts selection_length time
1771 adjust = selection_length;
1772 // ripple the new current playlist
1773 tv->playlist()->ripple (where, amount+adjust, exclude);
1775 // add regions after point where drag entered this track to subsequent ripples
1776 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1779 // motion on same track
1780 RegionMoveDrag::motion(event, first_move);
1784 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1785 prev_position = where;
1787 // selection encompasses multiple tracks - just drag
1788 // cross-track drags are forbidden
1789 RegionMoveDrag::motion(event, first_move);
1792 if (!_x_constrained) {
1793 prev_amount += amount;
1796 _last_frame_position = after;
1800 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1802 if (!movement_occurred) {
1806 if (was_double_click() && !_views.empty()) {
1807 DraggingView dv = _views.front();
1808 dv.view->show_region_editor ();
1815 _editor->begin_reversible_command(_("Ripple drag"));
1817 // remove the regions being rippled from the dragging view, updating them to
1818 // their new positions
1819 remove_unselected_from_views (prev_amount, true);
1821 if (allow_moves_across_tracks) {
1823 // if regions were dragged across tracks, we've rippled any later
1824 // regions on the track the regions were dragged off, so we need
1825 // to add the original track to the undo record
1826 orig_tav->playlist()->clear_changes();
1827 vector<Command*> cmds;
1828 orig_tav->playlist()->rdiff (cmds);
1829 _editor->session()->add_commands (cmds);
1831 if (prev_tav && prev_tav != orig_tav) {
1832 prev_tav->playlist()->clear_changes();
1833 vector<Command*> cmds;
1834 prev_tav->playlist()->rdiff (cmds);
1835 _editor->session()->add_commands (cmds);
1838 // selection spanned multiple tracks - all will need adding to undo record
1840 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1841 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1843 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1844 (*pi)->clear_changes();
1845 vector<Command*> cmds;
1846 (*pi)->rdiff (cmds);
1847 _editor->session()->add_commands (cmds);
1851 // other modified playlists are added to undo by RegionMoveDrag::finished()
1852 RegionMoveDrag::finished (event, movement_occurred);
1853 _editor->commit_reversible_command();
1857 RegionRippleDrag::aborted (bool movement_occurred)
1859 RegionMoveDrag::aborted (movement_occurred);
1864 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1866 _view (dynamic_cast<MidiTimeAxisView*> (v))
1868 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1874 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1877 _region = add_midi_region (_view);
1878 _view->playlist()->freeze ();
1881 framepos_t const f = adjusted_current_frame (event);
1882 if (f < grab_frame()) {
1883 _region->set_position (f);
1886 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1887 so that if this region is duplicated, its duplicate starts on
1888 a snap point rather than 1 frame after a snap point. Otherwise things get
1889 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1890 place snapped notes at the start of the region.
1893 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1894 _region->set_length (len < 1 ? 1 : len);
1900 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1902 if (!movement_occurred) {
1903 add_midi_region (_view);
1905 _view->playlist()->thaw ();
1910 RegionCreateDrag::aborted (bool)
1913 _view->playlist()->thaw ();
1919 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1923 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1927 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1929 Gdk::Cursor* cursor;
1930 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1932 float x_fraction = cnote->mouse_x_fraction ();
1934 if (x_fraction > 0.0 && x_fraction < 0.25) {
1935 cursor = _editor->cursors()->left_side_trim;
1938 cursor = _editor->cursors()->right_side_trim;
1942 Drag::start_grab (event, cursor);
1944 region = &cnote->region_view();
1948 if (event->motion.state & Keyboard::PrimaryModifier) {
1954 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1956 if (ms.size() > 1) {
1957 /* has to be relative, may make no sense otherwise */
1961 /* select this note; if it is already selected, preserve the existing selection,
1962 otherwise make this note the only one selected.
1964 region->note_selected (cnote, cnote->selected ());
1966 _editor->begin_reversible_command (_("resize notes"));
1968 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1969 MidiRegionSelection::iterator next;
1972 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1974 mrv->begin_resizing (at_front);
1981 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1983 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1984 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1985 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1987 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1989 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1995 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1997 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1998 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1999 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2001 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2003 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2007 _editor->commit_reversible_command ();
2011 NoteResizeDrag::aborted (bool)
2013 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2014 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2015 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2017 mrv->abort_resizing ();
2022 AVDraggingView::AVDraggingView (RegionView* v)
2025 initial_position = v->region()->position ();
2028 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2031 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2034 TrackViewList empty;
2036 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2037 std::list<RegionView*> views = rs.by_layer();
2039 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2040 RegionView* rv = (*i);
2041 if (!rv->region()->video_locked()) {
2044 _views.push_back (AVDraggingView (rv));
2049 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2051 Drag::start_grab (event);
2052 if (_editor->session() == 0) {
2056 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2057 _max_backwards_drag = (
2058 ARDOUR_UI::instance()->video_timeline->get_duration()
2059 + ARDOUR_UI::instance()->video_timeline->get_offset()
2060 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2063 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2064 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2065 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2068 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2071 Timecode::Time timecode;
2072 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2073 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);
2074 show_verbose_cursor_text (buf);
2078 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2080 if (_editor->session() == 0) {
2083 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2087 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2088 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2090 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2091 dt = - _max_backwards_drag;
2094 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2095 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2097 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2098 RegionView* rv = i->view;
2099 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2102 rv->region()->clear_changes ();
2103 rv->region()->suspend_property_changes();
2105 rv->region()->set_position(i->initial_position + dt);
2106 rv->region_changed(ARDOUR::Properties::position);
2109 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2110 Timecode::Time timecode;
2111 Timecode::Time timediff;
2113 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2114 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2115 snprintf (buf, sizeof (buf),
2116 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2117 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2118 , _("Video Start:"),
2119 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2121 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2123 show_verbose_cursor_text (buf);
2127 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2129 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2133 if (!movement_occurred || ! _editor->session()) {
2137 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2139 _editor->begin_reversible_command (_("Move Video"));
2141 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2142 ARDOUR_UI::instance()->video_timeline->save_undo();
2143 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2144 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2146 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2147 i->view->drag_end();
2148 i->view->region()->resume_property_changes ();
2150 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2153 _editor->session()->maybe_update_session_range(
2154 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2155 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2159 _editor->commit_reversible_command ();
2163 VideoTimeLineDrag::aborted (bool)
2165 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2168 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2169 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2171 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2172 i->view->region()->resume_property_changes ();
2173 i->view->region()->set_position(i->initial_position);
2177 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2178 : RegionDrag (e, i, p, v)
2179 , _preserve_fade_anchor (preserve_fade_anchor)
2180 , _jump_position_when_done (false)
2182 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2186 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2189 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2190 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2192 if (tv && tv->is_track()) {
2193 speed = tv->track()->speed();
2196 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2197 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2198 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2200 framepos_t const pf = adjusted_current_frame (event);
2202 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2203 /* Move the contents of the region around without changing the region bounds */
2204 _operation = ContentsTrim;
2205 Drag::start_grab (event, _editor->cursors()->trimmer);
2207 /* These will get overridden for a point trim.*/
2208 if (pf < (region_start + region_length/2)) {
2209 /* closer to front */
2210 _operation = StartTrim;
2212 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2213 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2215 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2219 _operation = EndTrim;
2220 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2221 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2223 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2228 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2229 _jump_position_when_done = true;
2232 switch (_operation) {
2234 show_verbose_cursor_time (region_start);
2235 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2236 i->view->trim_front_starting ();
2240 show_verbose_cursor_time (region_end);
2243 show_verbose_cursor_time (pf);
2247 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2248 i->view->region()->suspend_property_changes ();
2253 TrimDrag::motion (GdkEvent* event, bool first_move)
2255 RegionView* rv = _primary;
2258 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2259 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2260 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2261 frameoffset_t frame_delta = 0;
2263 if (tv && tv->is_track()) {
2264 speed = tv->track()->speed();
2267 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2273 switch (_operation) {
2275 trim_type = "Region start trim";
2278 trim_type = "Region end trim";
2281 trim_type = "Region content trim";
2288 _editor->begin_reversible_command (trim_type);
2290 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2291 RegionView* rv = i->view;
2292 rv->enable_display (false);
2293 rv->region()->playlist()->clear_owned_changes ();
2295 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2298 arv->temporarily_hide_envelope ();
2302 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2303 insert_result = _editor->motion_frozen_playlists.insert (pl);
2305 if (insert_result.second) {
2311 bool non_overlap_trim = false;
2313 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2314 non_overlap_trim = true;
2317 /* contstrain trim to fade length */
2318 if (_preserve_fade_anchor) {
2319 switch (_operation) {
2321 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2322 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2324 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2325 if (ar->locked()) continue;
2326 framecnt_t len = ar->fade_in()->back()->when;
2327 if (len < dt) dt = min(dt, len);
2331 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2332 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2334 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2335 if (ar->locked()) continue;
2336 framecnt_t len = ar->fade_out()->back()->when;
2337 if (len < -dt) dt = max(dt, -len);
2346 switch (_operation) {
2348 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2349 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2350 if (changed && _preserve_fade_anchor) {
2351 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2353 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2354 framecnt_t len = ar->fade_in()->back()->when;
2355 framecnt_t diff = ar->first_frame() - i->initial_position;
2356 framepos_t new_length = len - diff;
2357 i->anchored_fade_length = min (ar->length(), new_length);
2358 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2359 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2366 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2367 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2368 if (changed && _preserve_fade_anchor) {
2369 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2371 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2372 framecnt_t len = ar->fade_out()->back()->when;
2373 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2374 framepos_t new_length = len + diff;
2375 i->anchored_fade_length = min (ar->length(), new_length);
2376 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2377 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2385 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2387 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2388 i->view->move_contents (frame_delta);
2394 switch (_operation) {
2396 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2399 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2402 // show_verbose_cursor_time (frame_delta);
2409 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2411 if (movement_occurred) {
2412 motion (event, false);
2414 if (_operation == StartTrim) {
2415 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2417 /* This must happen before the region's StatefulDiffCommand is created, as it may
2418 `correct' (ahem) the region's _start from being negative to being zero. It
2419 needs to be zero in the undo record.
2421 i->view->trim_front_ending ();
2423 if (_preserve_fade_anchor) {
2424 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2426 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2427 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2428 ar->set_fade_in_length(i->anchored_fade_length);
2429 ar->set_fade_in_active(true);
2432 if (_jump_position_when_done) {
2433 i->view->region()->set_position (i->initial_position);
2436 } else if (_operation == EndTrim) {
2437 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2438 if (_preserve_fade_anchor) {
2439 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2441 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2442 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2443 ar->set_fade_out_length(i->anchored_fade_length);
2444 ar->set_fade_out_active(true);
2447 if (_jump_position_when_done) {
2448 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2453 if (!_views.empty()) {
2454 if (_operation == StartTrim) {
2455 _editor->maybe_locate_with_edit_preroll(
2456 _views.begin()->view->region()->position());
2458 if (_operation == EndTrim) {
2459 _editor->maybe_locate_with_edit_preroll(
2460 _views.begin()->view->region()->position() +
2461 _views.begin()->view->region()->length());
2465 if (!_editor->selection->selected (_primary)) {
2466 _primary->thaw_after_trim ();
2469 set<boost::shared_ptr<Playlist> > diffed_playlists;
2471 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2472 i->view->thaw_after_trim ();
2473 i->view->enable_display (true);
2475 /* Trimming one region may affect others on the playlist, so we need
2476 to get undo Commands from the whole playlist rather than just the
2477 region. Use diffed_playlists to make sure we don't diff a given
2478 playlist more than once.
2480 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2481 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2482 vector<Command*> cmds;
2484 _editor->session()->add_commands (cmds);
2485 diffed_playlists.insert (p);
2490 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2494 _editor->motion_frozen_playlists.clear ();
2495 _editor->commit_reversible_command();
2498 /* no mouse movement */
2499 _editor->point_trim (event, adjusted_current_frame (event));
2502 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2503 if (_operation == StartTrim) {
2504 i->view->trim_front_ending ();
2507 i->view->region()->resume_property_changes ();
2512 TrimDrag::aborted (bool movement_occurred)
2514 /* Our motion method is changing model state, so use the Undo system
2515 to cancel. Perhaps not ideal, as this will leave an Undo point
2516 behind which may be slightly odd from the user's point of view.
2521 if (movement_occurred) {
2525 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2526 i->view->region()->resume_property_changes ();
2531 TrimDrag::setup_pointer_frame_offset ()
2533 list<DraggingView>::iterator i = _views.begin ();
2534 while (i != _views.end() && i->view != _primary) {
2538 if (i == _views.end()) {
2542 switch (_operation) {
2544 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2547 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2554 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2558 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2559 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2564 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2566 Drag::start_grab (event, cursor);
2567 show_verbose_cursor_time (adjusted_current_frame(event));
2571 MeterMarkerDrag::setup_pointer_frame_offset ()
2573 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2577 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2579 if (!_marker->meter().movable()) {
2585 // create a dummy marker for visual representation of moving the
2586 // section, because whether its a copy or not, we're going to
2587 // leave or lose the original marker (leave if its a copy; lose if its
2588 // not, because we'll remove it from the map).
2590 MeterSection section (_marker->meter());
2592 if (!section.movable()) {
2597 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2599 _marker = new MeterMarker (
2601 *_editor->meter_group,
2602 ARDOUR_UI::config()->color ("meter marker"),
2604 *new MeterSection (_marker->meter())
2607 /* use the new marker for the grab */
2608 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2611 TempoMap& map (_editor->session()->tempo_map());
2612 /* get current state */
2613 before_state = &map.get_state();
2614 /* remove the section while we drag it */
2615 map.remove_meter (section, true);
2619 framepos_t const pf = adjusted_current_frame (event);
2621 _marker->set_position (pf);
2622 show_verbose_cursor_time (pf);
2626 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2628 if (!movement_occurred) {
2629 if (was_double_click()) {
2630 _editor->edit_meter_marker (*_marker);
2635 if (!_marker->meter().movable()) {
2639 motion (event, false);
2641 Timecode::BBT_Time when;
2643 TempoMap& map (_editor->session()->tempo_map());
2644 map.bbt_time (last_pointer_frame(), when);
2646 if (_copy == true) {
2647 _editor->begin_reversible_command (_("copy meter mark"));
2648 XMLNode &before = map.get_state();
2649 map.add_meter (_marker->meter(), when);
2650 XMLNode &after = map.get_state();
2651 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2652 _editor->commit_reversible_command ();
2655 _editor->begin_reversible_command (_("move meter mark"));
2657 /* we removed it before, so add it back now */
2659 map.add_meter (_marker->meter(), when);
2660 XMLNode &after = map.get_state();
2661 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2662 _editor->commit_reversible_command ();
2665 // delete the dummy marker we used for visual representation while moving.
2666 // a new visual marker will show up automatically.
2671 MeterMarkerDrag::aborted (bool moved)
2673 _marker->set_position (_marker->meter().frame ());
2676 TempoMap& map (_editor->session()->tempo_map());
2677 /* we removed it before, so add it back now */
2678 map.add_meter (_marker->meter(), _marker->meter().frame());
2679 // delete the dummy marker we used for visual representation while moving.
2680 // a new visual marker will show up automatically.
2685 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2689 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2691 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2696 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2698 Drag::start_grab (event, cursor);
2699 show_verbose_cursor_time (adjusted_current_frame (event));
2703 TempoMarkerDrag::setup_pointer_frame_offset ()
2705 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2709 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2711 if (!_marker->tempo().movable()) {
2717 // create a dummy marker for visual representation of moving the
2718 // section, because whether its a copy or not, we're going to
2719 // leave or lose the original marker (leave if its a copy; lose if its
2720 // not, because we'll remove it from the map).
2722 // create a dummy marker for visual representation of moving the copy.
2723 // The actual copying is not done before we reach the finish callback.
2726 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2728 TempoSection section (_marker->tempo());
2730 _marker = new TempoMarker (
2732 *_editor->tempo_group,
2733 ARDOUR_UI::config()->color ("tempo marker"),
2735 *new TempoSection (_marker->tempo())
2738 /* use the new marker for the grab */
2739 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2742 TempoMap& map (_editor->session()->tempo_map());
2743 /* get current state */
2744 before_state = &map.get_state();
2745 /* remove the section while we drag it */
2746 map.remove_tempo (section, true);
2750 framepos_t const pf = adjusted_current_frame (event);
2751 _marker->set_position (pf);
2752 show_verbose_cursor_time (pf);
2756 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2758 if (!movement_occurred) {
2759 if (was_double_click()) {
2760 _editor->edit_tempo_marker (*_marker);
2765 if (!_marker->tempo().movable()) {
2769 motion (event, false);
2771 TempoMap& map (_editor->session()->tempo_map());
2772 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2773 Timecode::BBT_Time when;
2775 map.bbt_time (beat_time, when);
2777 if (_copy == true) {
2778 _editor->begin_reversible_command (_("copy tempo mark"));
2779 XMLNode &before = map.get_state();
2780 map.add_tempo (_marker->tempo(), when);
2781 XMLNode &after = map.get_state();
2782 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2783 _editor->commit_reversible_command ();
2786 _editor->begin_reversible_command (_("move tempo mark"));
2787 /* we removed it before, so add it back now */
2788 map.add_tempo (_marker->tempo(), when);
2789 XMLNode &after = map.get_state();
2790 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2791 _editor->commit_reversible_command ();
2794 // delete the dummy marker we used for visual representation while moving.
2795 // a new visual marker will show up automatically.
2800 TempoMarkerDrag::aborted (bool moved)
2802 _marker->set_position (_marker->tempo().frame());
2804 TempoMap& map (_editor->session()->tempo_map());
2805 /* we removed it before, so add it back now */
2806 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2807 // delete the dummy marker we used for visual representation while moving.
2808 // a new visual marker will show up automatically.
2813 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2814 : Drag (e, &c.track_canvas_item(), false)
2818 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2821 /** Do all the things we do when dragging the playhead to make it look as though
2822 * we have located, without actually doing the locate (because that would cause
2823 * the diskstream buffers to be refilled, which is too slow).
2826 CursorDrag::fake_locate (framepos_t t)
2828 _editor->playhead_cursor->set_position (t);
2830 Session* s = _editor->session ();
2831 if (s->timecode_transmission_suspended ()) {
2832 framepos_t const f = _editor->playhead_cursor->current_frame ();
2833 /* This is asynchronous so it will be sent "now"
2835 s->send_mmc_locate (f);
2836 /* These are synchronous and will be sent during the next
2839 s->queue_full_time_code ();
2840 s->queue_song_position_pointer ();
2843 show_verbose_cursor_time (t);
2844 _editor->UpdateAllTransportClocks (t);
2848 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2850 Drag::start_grab (event, c);
2852 _grab_zoom = _editor->samples_per_pixel;
2854 framepos_t where = _editor->canvas_event_sample (event);
2856 _editor->snap_to_with_modifier (where, event);
2858 _editor->_dragging_playhead = true;
2860 Session* s = _editor->session ();
2862 /* grab the track canvas item as well */
2864 _cursor.track_canvas_item().grab();
2867 if (_was_rolling && _stop) {
2871 if (s->is_auditioning()) {
2872 s->cancel_audition ();
2876 if (AudioEngine::instance()->connected()) {
2878 /* do this only if we're the engine is connected
2879 * because otherwise this request will never be
2880 * serviced and we'll busy wait forever. likewise,
2881 * notice if we are disconnected while waiting for the
2882 * request to be serviced.
2885 s->request_suspend_timecode_transmission ();
2886 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2887 /* twiddle our thumbs */
2892 fake_locate (where);
2896 CursorDrag::motion (GdkEvent* event, bool)
2898 framepos_t const adjusted_frame = adjusted_current_frame (event);
2899 if (adjusted_frame != last_pointer_frame()) {
2900 fake_locate (adjusted_frame);
2905 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2907 _editor->_dragging_playhead = false;
2909 _cursor.track_canvas_item().ungrab();
2911 if (!movement_occurred && _stop) {
2915 motion (event, false);
2917 Session* s = _editor->session ();
2919 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2920 _editor->_pending_locate_request = true;
2921 s->request_resume_timecode_transmission ();
2926 CursorDrag::aborted (bool)
2928 _cursor.track_canvas_item().ungrab();
2930 if (_editor->_dragging_playhead) {
2931 _editor->session()->request_resume_timecode_transmission ();
2932 _editor->_dragging_playhead = false;
2935 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2938 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2939 : RegionDrag (e, i, p, v)
2941 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2945 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2947 Drag::start_grab (event, cursor);
2949 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2950 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2952 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2956 FadeInDrag::setup_pointer_frame_offset ()
2958 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2959 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2960 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2964 FadeInDrag::motion (GdkEvent* event, bool)
2966 framecnt_t fade_length;
2968 framepos_t const pos = adjusted_current_frame (event);
2970 boost::shared_ptr<Region> region = _primary->region ();
2972 if (pos < (region->position() + 64)) {
2973 fade_length = 64; // this should be a minimum defined somewhere
2974 } else if (pos > region->last_frame()) {
2975 fade_length = region->length();
2977 fade_length = pos - region->position();
2980 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2982 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2988 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2991 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2995 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2997 if (!movement_occurred) {
3001 framecnt_t fade_length;
3003 framepos_t const pos = adjusted_current_frame (event);
3005 boost::shared_ptr<Region> region = _primary->region ();
3007 if (pos < (region->position() + 64)) {
3008 fade_length = 64; // this should be a minimum defined somewhere
3009 } else if (pos > region->last_frame()) {
3010 fade_length = region->length();
3012 fade_length = pos - region->position();
3015 _editor->begin_reversible_command (_("change fade in length"));
3017 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3019 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3025 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3026 XMLNode &before = alist->get_state();
3028 tmp->audio_region()->set_fade_in_length (fade_length);
3029 tmp->audio_region()->set_fade_in_active (true);
3031 XMLNode &after = alist->get_state();
3032 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3035 _editor->commit_reversible_command ();
3039 FadeInDrag::aborted (bool)
3041 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3042 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3048 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3052 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3053 : RegionDrag (e, i, p, v)
3055 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3059 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3061 Drag::start_grab (event, cursor);
3063 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3064 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3066 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3070 FadeOutDrag::setup_pointer_frame_offset ()
3072 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3073 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3074 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3078 FadeOutDrag::motion (GdkEvent* event, bool)
3080 framecnt_t fade_length;
3082 framepos_t const pos = adjusted_current_frame (event);
3084 boost::shared_ptr<Region> region = _primary->region ();
3086 if (pos > (region->last_frame() - 64)) {
3087 fade_length = 64; // this should really be a minimum fade defined somewhere
3089 else if (pos < region->position()) {
3090 fade_length = region->length();
3093 fade_length = region->last_frame() - pos;
3096 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3098 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3104 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3107 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3111 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3113 if (!movement_occurred) {
3117 framecnt_t fade_length;
3119 framepos_t const pos = adjusted_current_frame (event);
3121 boost::shared_ptr<Region> region = _primary->region ();
3123 if (pos > (region->last_frame() - 64)) {
3124 fade_length = 64; // this should really be a minimum fade defined somewhere
3126 else if (pos < region->position()) {
3127 fade_length = region->length();
3130 fade_length = region->last_frame() - pos;
3133 _editor->begin_reversible_command (_("change fade out length"));
3135 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3137 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3143 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3144 XMLNode &before = alist->get_state();
3146 tmp->audio_region()->set_fade_out_length (fade_length);
3147 tmp->audio_region()->set_fade_out_active (true);
3149 XMLNode &after = alist->get_state();
3150 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3153 _editor->commit_reversible_command ();
3157 FadeOutDrag::aborted (bool)
3159 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3160 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3166 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3170 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3173 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3175 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3178 _points.push_back (ArdourCanvas::Duple (0, 0));
3179 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3182 MarkerDrag::~MarkerDrag ()
3184 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3189 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3191 location = new Location (*l);
3192 markers.push_back (m);
3197 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3199 Drag::start_grab (event, cursor);
3203 Location *location = _editor->find_location_from_marker (_marker, is_start);
3204 _editor->_dragging_edit_point = true;
3206 update_item (location);
3208 // _drag_line->show();
3209 // _line->raise_to_top();
3212 show_verbose_cursor_time (location->start());
3214 show_verbose_cursor_time (location->end());
3217 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3220 case Selection::Toggle:
3221 /* we toggle on the button release */
3223 case Selection::Set:
3224 if (!_editor->selection->selected (_marker)) {
3225 _editor->selection->set (_marker);
3228 case Selection::Extend:
3230 Locations::LocationList ll;
3231 list<Marker*> to_add;
3233 _editor->selection->markers.range (s, e);
3234 s = min (_marker->position(), s);
3235 e = max (_marker->position(), e);
3238 if (e < max_framepos) {
3241 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3242 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3243 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3246 to_add.push_back (lm->start);
3249 to_add.push_back (lm->end);
3253 if (!to_add.empty()) {
3254 _editor->selection->add (to_add);
3258 case Selection::Add:
3259 _editor->selection->add (_marker);
3263 /* Set up copies for us to manipulate during the drag
3266 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3268 Location* l = _editor->find_location_from_marker (*i, is_start);
3275 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3277 /* range: check that the other end of the range isn't
3280 CopiedLocationInfo::iterator x;
3281 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3282 if (*(*x).location == *l) {
3286 if (x == _copied_locations.end()) {
3287 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3289 (*x).markers.push_back (*i);
3290 (*x).move_both = true;
3298 MarkerDrag::setup_pointer_frame_offset ()
3301 Location *location = _editor->find_location_from_marker (_marker, is_start);
3302 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3306 MarkerDrag::motion (GdkEvent* event, bool)
3308 framecnt_t f_delta = 0;
3310 bool move_both = false;
3311 Location *real_location;
3312 Location *copy_location = 0;
3314 framepos_t const newframe = adjusted_current_frame (event);
3315 framepos_t next = newframe;
3317 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3321 CopiedLocationInfo::iterator x;
3323 /* find the marker we're dragging, and compute the delta */
3325 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3327 copy_location = (*x).location;
3329 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3331 /* this marker is represented by this
3332 * CopiedLocationMarkerInfo
3335 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3340 if (real_location->is_mark()) {
3341 f_delta = newframe - copy_location->start();
3345 switch (_marker->type()) {
3346 case Marker::SessionStart:
3347 case Marker::RangeStart:
3348 case Marker::LoopStart:
3349 case Marker::PunchIn:
3350 f_delta = newframe - copy_location->start();
3353 case Marker::SessionEnd:
3354 case Marker::RangeEnd:
3355 case Marker::LoopEnd:
3356 case Marker::PunchOut:
3357 f_delta = newframe - copy_location->end();
3360 /* what kind of marker is this ? */
3369 if (x == _copied_locations.end()) {
3370 /* hmm, impossible - we didn't find the dragged marker */
3374 /* now move them all */
3376 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3378 copy_location = x->location;
3380 /* call this to find out if its the start or end */
3382 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3386 if (real_location->locked()) {
3390 if (copy_location->is_mark()) {
3394 copy_location->set_start (copy_location->start() + f_delta);
3398 framepos_t new_start = copy_location->start() + f_delta;
3399 framepos_t new_end = copy_location->end() + f_delta;
3401 if (is_start) { // start-of-range marker
3403 if (move_both || (*x).move_both) {
3404 copy_location->set_start (new_start);
3405 copy_location->set_end (new_end);
3406 } else if (new_start < copy_location->end()) {
3407 copy_location->set_start (new_start);
3408 } else if (newframe > 0) {
3409 _editor->snap_to (next, RoundUpAlways, true);
3410 copy_location->set_end (next);
3411 copy_location->set_start (newframe);
3414 } else { // end marker
3416 if (move_both || (*x).move_both) {
3417 copy_location->set_end (new_end);
3418 copy_location->set_start (new_start);
3419 } else if (new_end > copy_location->start()) {
3420 copy_location->set_end (new_end);
3421 } else if (newframe > 0) {
3422 _editor->snap_to (next, RoundDownAlways, true);
3423 copy_location->set_start (next);
3424 copy_location->set_end (newframe);
3429 update_item (copy_location);
3431 /* now lookup the actual GUI items used to display this
3432 * location and move them to wherever the copy of the location
3433 * is now. This means that the logic in ARDOUR::Location is
3434 * still enforced, even though we are not (yet) modifying
3435 * the real Location itself.
3438 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3441 lm->set_position (copy_location->start(), copy_location->end());
3446 assert (!_copied_locations.empty());
3448 show_verbose_cursor_time (newframe);
3452 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3454 if (!movement_occurred) {
3456 if (was_double_click()) {
3457 _editor->rename_marker (_marker);
3461 /* just a click, do nothing but finish
3462 off the selection process
3465 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3468 case Selection::Set:
3469 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3470 _editor->selection->set (_marker);
3474 case Selection::Toggle:
3475 /* we toggle on the button release, click only */
3476 _editor->selection->toggle (_marker);
3479 case Selection::Extend:
3480 case Selection::Add:
3487 _editor->_dragging_edit_point = false;
3489 _editor->begin_reversible_command ( _("move marker") );
3490 XMLNode &before = _editor->session()->locations()->get_state();
3492 MarkerSelection::iterator i;
3493 CopiedLocationInfo::iterator x;
3496 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3497 x != _copied_locations.end() && i != _editor->selection->markers.end();
3500 Location * location = _editor->find_location_from_marker (*i, is_start);
3504 if (location->locked()) {
3508 if (location->is_mark()) {
3509 location->set_start (((*x).location)->start());
3511 location->set (((*x).location)->start(), ((*x).location)->end());
3516 XMLNode &after = _editor->session()->locations()->get_state();
3517 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3518 _editor->commit_reversible_command ();
3522 MarkerDrag::aborted (bool)
3528 MarkerDrag::update_item (Location*)
3533 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3535 _cumulative_x_drag (0),
3536 _cumulative_y_drag (0)
3538 if (_zero_gain_fraction < 0.0) {
3539 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3542 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3544 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3550 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3552 Drag::start_grab (event, _editor->cursors()->fader);
3554 // start the grab at the center of the control point so
3555 // the point doesn't 'jump' to the mouse after the first drag
3556 _fixed_grab_x = _point->get_x();
3557 _fixed_grab_y = _point->get_y();
3559 float const fraction = 1 - (_point->get_y() / _point->line().height());
3561 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3563 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3565 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3567 if (!_point->can_slide ()) {
3568 _x_constrained = true;
3573 ControlPointDrag::motion (GdkEvent* event, bool)
3575 double dx = _drags->current_pointer_x() - last_pointer_x();
3576 double dy = current_pointer_y() - last_pointer_y();
3578 if (event->button.state & Keyboard::SecondaryModifier) {
3583 /* coordinate in pixels relative to the start of the region (for region-based automation)
3584 or track (for track-based automation) */
3585 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3586 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3588 // calculate zero crossing point. back off by .01 to stay on the
3589 // positive side of zero
3590 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3592 // make sure we hit zero when passing through
3593 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3597 if (_x_constrained) {
3600 if (_y_constrained) {
3604 _cumulative_x_drag = cx - _fixed_grab_x;
3605 _cumulative_y_drag = cy - _fixed_grab_y;
3609 cy = min ((double) _point->line().height(), cy);
3611 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3613 if (!_x_constrained) {
3614 _editor->snap_to_with_modifier (cx_frames, event);
3617 cx_frames = min (cx_frames, _point->line().maximum_time());
3619 float const fraction = 1.0 - (cy / _point->line().height());
3621 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3623 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3627 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3629 if (!movement_occurred) {
3633 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3634 _editor->reset_point_selection ();
3638 motion (event, false);
3641 _point->line().end_drag (_pushing, _final_index);
3642 _editor->commit_reversible_command ();
3646 ControlPointDrag::aborted (bool)
3648 _point->line().reset ();
3652 ControlPointDrag::active (Editing::MouseMode m)
3654 if (m == Editing::MouseDraw) {
3655 /* always active in mouse draw */
3659 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3660 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3663 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3666 _cumulative_y_drag (0)
3668 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3672 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3674 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3677 _item = &_line->grab_item ();
3679 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3680 origin, and ditto for y.
3683 double cx = event->button.x;
3684 double cy = event->button.y;
3686 _line->parent_group().canvas_to_item (cx, cy);
3688 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3693 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3694 /* no adjacent points */
3698 Drag::start_grab (event, _editor->cursors()->fader);
3700 /* store grab start in parent frame */
3705 double fraction = 1.0 - (cy / _line->height());
3707 _line->start_drag_line (before, after, fraction);
3709 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3713 LineDrag::motion (GdkEvent* event, bool)
3715 double dy = current_pointer_y() - last_pointer_y();
3717 if (event->button.state & Keyboard::SecondaryModifier) {
3721 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3723 _cumulative_y_drag = cy - _fixed_grab_y;
3726 cy = min ((double) _line->height(), cy);
3728 double const fraction = 1.0 - (cy / _line->height());
3731 /* we are ignoring x position for this drag, so we can just pass in anything */
3732 _line->drag_motion (0, fraction, true, false, ignored);
3734 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3738 LineDrag::finished (GdkEvent* event, bool movement_occured)
3740 if (movement_occured) {
3741 motion (event, false);
3742 _line->end_drag (false, 0);
3744 /* add a new control point on the line */
3746 AutomationTimeAxisView* atv;
3748 _line->end_drag (false, 0);
3750 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3751 framepos_t where = _editor->window_event_sample (event, 0, 0);
3752 atv->add_automation_event (event, where, event->button.y, false);
3756 _editor->commit_reversible_command ();
3760 LineDrag::aborted (bool)
3765 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3768 _cumulative_x_drag (0)
3770 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3774 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3776 Drag::start_grab (event);
3778 _line = reinterpret_cast<Line*> (_item);
3781 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3783 double cx = event->button.x;
3784 double cy = event->button.y;
3786 _item->parent()->canvas_to_item (cx, cy);
3788 /* store grab start in parent frame */
3789 _region_view_grab_x = cx;
3791 _before = *(float*) _item->get_data ("position");
3793 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3795 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3799 FeatureLineDrag::motion (GdkEvent*, bool)
3801 double dx = _drags->current_pointer_x() - last_pointer_x();
3803 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3805 _cumulative_x_drag += dx;
3807 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3816 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3818 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3820 float *pos = new float;
3823 _line->set_data ("position", pos);
3829 FeatureLineDrag::finished (GdkEvent*, bool)
3831 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3832 _arv->update_transient(_before, _before);
3836 FeatureLineDrag::aborted (bool)
3841 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3843 , _vertical_only (false)
3845 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3849 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3851 Drag::start_grab (event);
3852 show_verbose_cursor_time (adjusted_current_frame (event));
3856 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3863 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ());
3865 framepos_t grab = grab_frame ();
3866 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
3867 _editor->snap_to_with_modifier (grab, event);
3870 /* base start and end on initial click position */
3880 if (current_pointer_y() < grab_y()) {
3881 y1 = current_pointer_y();
3884 y2 = current_pointer_y();
3888 if (start != end || y1 != y2) {
3890 double x1 = _editor->sample_to_pixel (start);
3891 double x2 = _editor->sample_to_pixel (end);
3892 const double min_dimension = 2.0;
3894 if (_vertical_only) {
3895 /* fixed 10 pixel width */
3899 x2 = min (x1 - min_dimension, x2);
3901 x2 = max (x1 + min_dimension, x2);
3906 y2 = min (y1 - min_dimension, y2);
3908 y2 = max (y1 + min_dimension, y2);
3911 /* translate rect into item space and set */
3913 ArdourCanvas::Rect r (x1, y1, x2, y2);
3915 /* this drag is a _trackview_only == true drag, so the y1 and
3916 * y2 (computed using current_pointer_y() and grab_y()) will be
3917 * relative to the top of the trackview group). The
3918 * rubberband rect has the same parent/scroll offset as the
3919 * the trackview group, so we can use the "r" rect directly
3920 * to set the shape of the rubberband.
3923 _editor->rubberband_rect->set (r);
3924 _editor->rubberband_rect->show();
3925 _editor->rubberband_rect->raise_to_top();
3927 show_verbose_cursor_time (pf);
3929 do_select_things (event, true);
3934 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3939 if (grab_frame() < last_pointer_frame()) {
3941 x2 = last_pointer_frame ();
3944 x1 = last_pointer_frame ();
3950 if (current_pointer_y() < grab_y()) {
3951 y1 = current_pointer_y();
3954 y2 = current_pointer_y();
3958 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3962 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3964 if (movement_occurred) {
3966 motion (event, false);
3967 do_select_things (event, false);
3973 bool do_deselect = true;
3974 MidiTimeAxisView* mtv;
3976 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3978 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
3979 /* nothing selected */
3980 add_midi_region (mtv);
3981 do_deselect = false;
3985 /* do not deselect if Primary or Tertiary (toggle-select or
3986 * extend-select are pressed.
3989 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3990 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3997 _editor->rubberband_rect->hide();
4001 RubberbandSelectDrag::aborted (bool)
4003 _editor->rubberband_rect->hide ();
4006 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4007 : RegionDrag (e, i, p, v)
4009 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4013 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4015 Drag::start_grab (event, cursor);
4017 show_verbose_cursor_time (adjusted_current_frame (event));
4021 TimeFXDrag::motion (GdkEvent* event, bool)
4023 RegionView* rv = _primary;
4024 StreamView* cv = rv->get_time_axis_view().view ();
4026 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4027 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4028 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4030 framepos_t const pf = adjusted_current_frame (event);
4032 if (pf > rv->region()->position()) {
4033 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4036 show_verbose_cursor_time (pf);
4040 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4042 _primary->get_time_axis_view().hide_timestretch ();
4044 if (!movement_occurred) {
4048 if (last_pointer_frame() < _primary->region()->position()) {
4049 /* backwards drag of the left edge - not usable */
4053 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4055 float percentage = (double) newlen / (double) _primary->region()->length();
4057 #ifndef USE_RUBBERBAND
4058 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4059 if (_primary->region()->data_type() == DataType::AUDIO) {
4060 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4064 if (!_editor->get_selection().regions.empty()) {
4065 /* primary will already be included in the selection, and edit
4066 group shared editing will propagate selection across
4067 equivalent regions, so just use the current region
4071 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4072 error << _("An error occurred while executing time stretch operation") << endmsg;
4078 TimeFXDrag::aborted (bool)
4080 _primary->get_time_axis_view().hide_timestretch ();
4083 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4086 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4090 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4092 Drag::start_grab (event);
4096 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4098 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4102 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4104 if (movement_occurred && _editor->session()) {
4105 /* make sure we stop */
4106 _editor->session()->request_transport_speed (0.0);
4111 ScrubDrag::aborted (bool)
4116 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4120 , _original_pointer_time_axis (-1)
4121 , _time_selection_at_start (!_editor->get_selection().time.empty())
4123 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4125 if (_time_selection_at_start) {
4126 start_at_start = _editor->get_selection().time.start();
4127 end_at_start = _editor->get_selection().time.end_frame();
4132 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4134 if (_editor->session() == 0) {
4138 Gdk::Cursor* cursor = 0;
4140 switch (_operation) {
4141 case CreateSelection:
4142 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4147 cursor = _editor->cursors()->selector;
4148 Drag::start_grab (event, cursor);
4151 case SelectionStartTrim:
4152 if (_editor->clicked_axisview) {
4153 _editor->clicked_axisview->order_selection_trims (_item, true);
4155 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4158 case SelectionEndTrim:
4159 if (_editor->clicked_axisview) {
4160 _editor->clicked_axisview->order_selection_trims (_item, false);
4162 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4166 Drag::start_grab (event, cursor);
4169 case SelectionExtend:
4170 Drag::start_grab (event, cursor);
4174 if (_operation == SelectionMove) {
4175 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4177 show_verbose_cursor_time (adjusted_current_frame (event));
4180 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4184 SelectionDrag::setup_pointer_frame_offset ()
4186 switch (_operation) {
4187 case CreateSelection:
4188 _pointer_frame_offset = 0;
4191 case SelectionStartTrim:
4193 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4196 case SelectionEndTrim:
4197 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4200 case SelectionExtend:
4206 SelectionDrag::motion (GdkEvent* event, bool first_move)
4208 framepos_t start = 0;
4210 framecnt_t length = 0;
4211 framecnt_t distance = 0;
4213 framepos_t const pending_position = adjusted_current_frame (event);
4215 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4219 switch (_operation) {
4220 case CreateSelection:
4222 framepos_t grab = grab_frame ();
4225 grab = adjusted_current_frame (event, false);
4226 if (grab < pending_position) {
4227 _editor->snap_to (grab, RoundDownMaybe);
4229 _editor->snap_to (grab, RoundUpMaybe);
4233 if (pending_position < grab) {
4234 start = pending_position;
4237 end = pending_position;
4241 /* first drag: Either add to the selection
4242 or create a new selection
4249 /* adding to the selection */
4250 _editor->set_selected_track_as_side_effect (Selection::Add);
4251 _editor->clicked_selection = _editor->selection->add (start, end);
4258 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4259 _editor->set_selected_track_as_side_effect (Selection::Set);
4262 _editor->clicked_selection = _editor->selection->set (start, end);
4266 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4267 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4268 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4270 _editor->selection->add (atest);
4274 /* select all tracks within the rectangle that we've marked out so far */
4275 TrackViewList new_selection;
4276 TrackViewList& all_tracks (_editor->track_views);
4278 ArdourCanvas::Coord const top = grab_y();
4279 ArdourCanvas::Coord const bottom = current_pointer_y();
4281 if (top >= 0 && bottom >= 0) {
4283 //first, find the tracks that are covered in the y range selection
4284 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4285 if ((*i)->covered_by_y_range (top, bottom)) {
4286 new_selection.push_back (*i);
4290 //now find any tracks that are GROUPED with the tracks we selected
4291 TrackViewList grouped_add = new_selection;
4292 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4293 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4294 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4295 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4296 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4297 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4298 grouped_add.push_back (*j);
4303 //now compare our list with the current selection, and add or remove as necessary
4304 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4305 TrackViewList tracks_to_add;
4306 TrackViewList tracks_to_remove;
4307 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4308 if ( !_editor->selection->tracks.contains ( *i ) )
4309 tracks_to_add.push_back ( *i );
4310 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4311 if ( !grouped_add.contains ( *i ) )
4312 tracks_to_remove.push_back ( *i );
4313 _editor->selection->add(tracks_to_add);
4314 _editor->selection->remove(tracks_to_remove);
4320 case SelectionStartTrim:
4322 start = _editor->selection->time[_editor->clicked_selection].start;
4323 end = _editor->selection->time[_editor->clicked_selection].end;
4325 if (pending_position > end) {
4328 start = pending_position;
4332 case SelectionEndTrim:
4334 start = _editor->selection->time[_editor->clicked_selection].start;
4335 end = _editor->selection->time[_editor->clicked_selection].end;
4337 if (pending_position < start) {
4340 end = pending_position;
4347 start = _editor->selection->time[_editor->clicked_selection].start;
4348 end = _editor->selection->time[_editor->clicked_selection].end;
4350 length = end - start;
4351 distance = pending_position - start;
4352 start = pending_position;
4353 _editor->snap_to (start);
4355 end = start + length;
4359 case SelectionExtend:
4364 switch (_operation) {
4366 if (_time_selection_at_start) {
4367 _editor->selection->move_time (distance);
4371 _editor->selection->replace (_editor->clicked_selection, start, end);
4375 if (_operation == SelectionMove) {
4376 show_verbose_cursor_time(start);
4378 show_verbose_cursor_time(pending_position);
4383 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4385 Session* s = _editor->session();
4387 _editor->begin_reversible_selection_op (_("Change Time Selection"));
4388 if (movement_occurred) {
4389 motion (event, false);
4390 /* XXX this is not object-oriented programming at all. ick */
4391 if (_editor->selection->time.consolidate()) {
4392 _editor->selection->TimeChanged ();
4395 /* XXX what if its a music time selection? */
4397 if ( s->get_play_range() && s->transport_rolling() ) {
4398 s->request_play_range (&_editor->selection->time, true);
4400 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4401 if (_operation == SelectionEndTrim)
4402 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4404 s->request_locate (_editor->get_selection().time.start());
4410 /* just a click, no pointer movement.
4413 if (_operation == SelectionExtend) {
4414 if (_time_selection_at_start) {
4415 framepos_t pos = adjusted_current_frame (event, false);
4416 framepos_t start = min (pos, start_at_start);
4417 framepos_t end = max (pos, end_at_start);
4418 _editor->selection->set (start, end);
4421 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4422 if (_editor->clicked_selection) {
4423 _editor->selection->remove (_editor->clicked_selection);
4426 if (!_editor->clicked_selection) {
4427 _editor->selection->clear_time();
4432 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4433 _editor->selection->set (_editor->clicked_axisview);
4436 if (s && s->get_play_range () && s->transport_rolling()) {
4437 s->request_stop (false, false);
4442 _editor->stop_canvas_autoscroll ();
4443 _editor->clicked_selection = 0;
4444 _editor->commit_reversible_selection_op ();
4448 SelectionDrag::aborted (bool)
4453 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4454 : Drag (e, i, false),
4458 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4460 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4461 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4462 physical_screen_height (_editor->get_window())));
4463 _drag_rect->hide ();
4465 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4466 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4470 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4472 if (_editor->session() == 0) {
4476 Gdk::Cursor* cursor = 0;
4478 if (!_editor->temp_location) {
4479 _editor->temp_location = new Location (*_editor->session());
4482 switch (_operation) {
4483 case CreateSkipMarker:
4484 case CreateRangeMarker:
4485 case CreateTransportMarker:
4486 case CreateCDMarker:
4488 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4493 cursor = _editor->cursors()->selector;
4497 Drag::start_grab (event, cursor);
4499 show_verbose_cursor_time (adjusted_current_frame (event));
4503 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4505 framepos_t start = 0;
4507 ArdourCanvas::Rectangle *crect;
4509 switch (_operation) {
4510 case CreateSkipMarker:
4511 crect = _editor->range_bar_drag_rect;
4513 case CreateRangeMarker:
4514 crect = _editor->range_bar_drag_rect;
4516 case CreateTransportMarker:
4517 crect = _editor->transport_bar_drag_rect;
4519 case CreateCDMarker:
4520 crect = _editor->cd_marker_bar_drag_rect;
4523 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4528 framepos_t const pf = adjusted_current_frame (event);
4530 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4531 framepos_t grab = grab_frame ();
4532 _editor->snap_to (grab);
4534 if (pf < grab_frame()) {
4542 /* first drag: Either add to the selection
4543 or create a new selection.
4548 _editor->temp_location->set (start, end);
4552 update_item (_editor->temp_location);
4554 //_drag_rect->raise_to_top();
4560 _editor->temp_location->set (start, end);
4562 double x1 = _editor->sample_to_pixel (start);
4563 double x2 = _editor->sample_to_pixel (end);
4567 update_item (_editor->temp_location);
4570 show_verbose_cursor_time (pf);
4575 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4577 Location * newloc = 0;
4581 if (movement_occurred) {
4582 motion (event, false);
4585 switch (_operation) {
4586 case CreateSkipMarker:
4587 case CreateRangeMarker:
4588 case CreateCDMarker:
4590 XMLNode &before = _editor->session()->locations()->get_state();
4591 if (_operation == CreateSkipMarker) {
4592 _editor->begin_reversible_command (_("new skip marker"));
4593 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4594 flags = Location::IsRangeMarker | Location::IsSkip;
4595 _editor->range_bar_drag_rect->hide();
4596 } else if (_operation == CreateCDMarker) {
4597 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4598 _editor->begin_reversible_command (_("new CD marker"));
4599 flags = Location::IsRangeMarker | Location::IsCDMarker;
4600 _editor->cd_marker_bar_drag_rect->hide();
4602 _editor->begin_reversible_command (_("new skip marker"));
4603 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4604 flags = Location::IsRangeMarker;
4605 _editor->range_bar_drag_rect->hide();
4607 newloc = new Location (
4608 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4611 _editor->session()->locations()->add (newloc, true);
4612 XMLNode &after = _editor->session()->locations()->get_state();
4613 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4614 _editor->commit_reversible_command ();
4618 case CreateTransportMarker:
4619 // popup menu to pick loop or punch
4620 _editor->new_transport_marker_context_menu (&event->button, _item);
4626 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4628 if (_operation == CreateTransportMarker) {
4630 /* didn't drag, so just locate */
4632 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4634 } else if (_operation == CreateCDMarker) {
4636 /* didn't drag, but mark is already created so do
4639 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4645 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4647 if (end == max_framepos) {
4648 end = _editor->session()->current_end_frame ();
4651 if (start == max_framepos) {
4652 start = _editor->session()->current_start_frame ();
4655 switch (_editor->mouse_mode) {
4657 /* find the two markers on either side and then make the selection from it */
4658 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4662 /* find the two markers on either side of the click and make the range out of it */
4663 _editor->selection->set (start, end);
4672 _editor->stop_canvas_autoscroll ();
4676 RangeMarkerBarDrag::aborted (bool)
4682 RangeMarkerBarDrag::update_item (Location* location)
4684 double const x1 = _editor->sample_to_pixel (location->start());
4685 double const x2 = _editor->sample_to_pixel (location->end());
4687 _drag_rect->set_x0 (x1);
4688 _drag_rect->set_x1 (x2);
4691 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4693 , _cumulative_dx (0)
4694 , _cumulative_dy (0)
4696 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4698 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4700 _region = &_primary->region_view ();
4701 _note_height = _region->midi_stream_view()->note_height ();
4705 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4707 Drag::start_grab (event);
4709 if (!(_was_selected = _primary->selected())) {
4711 /* tertiary-click means extend selection - we'll do that on button release,
4712 so don't add it here, because otherwise we make it hard to figure
4713 out the "extend-to" range.
4716 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4719 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4722 _region->note_selected (_primary, true);
4724 _region->unique_select (_primary);
4727 _editor->begin_reversible_selection_op(_("Select Note Press"));
4728 _editor->commit_reversible_selection_op();
4733 /** @return Current total drag x change in frames */
4735 NoteDrag::total_dx () const
4738 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4740 /* primary note time */
4741 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4743 /* new time of the primary note in session frames */
4744 frameoffset_t st = n + dx;
4746 framepos_t const rp = _region->region()->position ();
4748 /* prevent the note being dragged earlier than the region's position */
4751 /* snap and return corresponding delta */
4752 return _region->snap_frame_to_frame (st - rp) + rp - n;
4755 /** @return Current total drag y change in note number */
4757 NoteDrag::total_dy () const
4759 MidiStreamView* msv = _region->midi_stream_view ();
4760 double const y = _region->midi_view()->y_position ();
4761 /* new current note */
4762 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4764 n = max (msv->lowest_note(), n);
4765 n = min (msv->highest_note(), n);
4766 /* and work out delta */
4767 return n - msv->y_to_note (grab_y() - y);
4771 NoteDrag::motion (GdkEvent *, bool)
4773 /* Total change in x and y since the start of the drag */
4774 frameoffset_t const dx = total_dx ();
4775 int8_t const dy = total_dy ();
4777 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4778 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4779 double const tdy = -dy * _note_height - _cumulative_dy;
4782 _cumulative_dx += tdx;
4783 _cumulative_dy += tdy;
4785 int8_t note_delta = total_dy();
4787 _region->move_selection (tdx, tdy, note_delta);
4789 /* the new note value may be the same as the old one, but we
4790 * don't know what that means because the selection may have
4791 * involved more than one note and we might be doing something
4792 * odd with them. so show the note value anyway, always.
4796 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4798 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4799 (int) floor ((double)new_note));
4801 show_verbose_cursor_text (buf);
4806 NoteDrag::finished (GdkEvent* ev, bool moved)
4809 /* no motion - select note */
4811 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4812 _editor->current_mouse_mode() == Editing::MouseDraw) {
4814 bool changed = false;
4816 if (_was_selected) {
4817 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4819 _region->note_deselected (_primary);
4823 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4824 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4826 if (!extend && !add && _region->selection_size() > 1) {
4827 _region->unique_select (_primary);
4829 } else if (extend) {
4830 _region->note_selected (_primary, true, true);
4833 /* it was added during button press */
4838 _editor->begin_reversible_selection_op(_("Select Note Release"));
4839 _editor->commit_reversible_selection_op();
4843 _region->note_dropped (_primary, total_dx(), total_dy());
4848 NoteDrag::aborted (bool)
4853 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4854 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4855 : Drag (editor, atv->base_item ())
4857 , _y_origin (atv->y_position())
4858 , _nothing_to_drag (false)
4860 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4861 setup (atv->lines ());
4864 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
4865 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
4866 : Drag (editor, rv->get_canvas_group ())
4868 , _y_origin (rv->get_time_axis_view().y_position())
4869 , _nothing_to_drag (false)
4872 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4874 list<boost::shared_ptr<AutomationLine> > lines;
4876 AudioRegionView* audio_view;
4877 AutomationRegionView* automation_view;
4878 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
4879 lines.push_back (audio_view->get_gain_line ());
4880 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
4881 lines.push_back (automation_view->line ());
4884 error << _("Automation range drag created for invalid region type") << endmsg;
4890 /** @param lines AutomationLines to drag.
4891 * @param offset Offset from the session start to the points in the AutomationLines.
4894 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4896 /* find the lines that overlap the ranges being dragged */
4897 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4898 while (i != lines.end ()) {
4899 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4902 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4904 /* check this range against all the AudioRanges that we are using */
4905 list<AudioRange>::const_iterator k = _ranges.begin ();
4906 while (k != _ranges.end()) {
4907 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4913 /* add it to our list if it overlaps at all */
4914 if (k != _ranges.end()) {
4919 _lines.push_back (n);
4925 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4929 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4931 return 1.0 - ((global_y - _y_origin) / line->height());
4935 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
4937 const double v = list->eval(x);
4938 return _integral ? rint(v) : v;
4942 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4944 Drag::start_grab (event, cursor);
4946 /* Get line states before we start changing things */
4947 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4948 i->state = &i->line->get_state ();
4949 i->original_fraction = y_fraction (i->line, current_pointer_y());
4952 if (_ranges.empty()) {
4954 /* No selected time ranges: drag all points */
4955 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4956 uint32_t const N = i->line->npoints ();
4957 for (uint32_t j = 0; j < N; ++j) {
4958 i->points.push_back (i->line->nth (j));
4964 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4966 framecnt_t const half = (i->start + i->end) / 2;
4968 /* find the line that this audio range starts in */
4969 list<Line>::iterator j = _lines.begin();
4970 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4974 if (j != _lines.end()) {
4975 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4977 /* j is the line that this audio range starts in; fade into it;
4978 64 samples length plucked out of thin air.
4981 framepos_t a = i->start + 64;
4986 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4987 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4989 the_list->editor_add (p, value (the_list, p));
4990 the_list->editor_add (q, value (the_list, q));
4993 /* same thing for the end */
4996 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5000 if (j != _lines.end()) {
5001 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5003 /* j is the line that this audio range starts in; fade out of it;
5004 64 samples length plucked out of thin air.
5007 framepos_t b = i->end - 64;
5012 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5013 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5015 the_list->editor_add (p, value (the_list, p));
5016 the_list->editor_add (q, value (the_list, q));
5020 _nothing_to_drag = true;
5022 /* Find all the points that should be dragged and put them in the relevant
5023 points lists in the Line structs.
5026 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5028 uint32_t const N = i->line->npoints ();
5029 for (uint32_t j = 0; j < N; ++j) {
5031 /* here's a control point on this line */
5032 ControlPoint* p = i->line->nth (j);
5033 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5035 /* see if it's inside a range */
5036 list<AudioRange>::const_iterator k = _ranges.begin ();
5037 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5041 if (k != _ranges.end()) {
5042 /* dragging this point */
5043 _nothing_to_drag = false;
5044 i->points.push_back (p);
5050 if (_nothing_to_drag) {
5054 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5055 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5060 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5062 if (_nothing_to_drag) {
5066 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5067 float const f = y_fraction (l->line, current_pointer_y());
5068 /* we are ignoring x position for this drag, so we can just pass in anything */
5070 l->line->drag_motion (0, f, true, false, ignored);
5071 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5076 AutomationRangeDrag::finished (GdkEvent* event, bool)
5078 if (_nothing_to_drag) {
5082 motion (event, false);
5083 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5084 i->line->end_drag (false, 0);
5087 _editor->commit_reversible_command ();
5091 AutomationRangeDrag::aborted (bool)
5093 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5098 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5100 , initial_time_axis_view (itav)
5102 /* note that time_axis_view may be null if the regionview was created
5103 * as part of a copy operation.
5105 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5106 layer = v->region()->layer ();
5107 initial_y = v->get_canvas_group()->position().y;
5108 initial_playlist = v->region()->playlist ();
5109 initial_position = v->region()->position ();
5110 initial_end = v->region()->position () + v->region()->length ();
5113 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5114 : Drag (e, i->canvas_item ())
5117 , _cumulative_dx (0)
5119 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5120 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5125 PatchChangeDrag::motion (GdkEvent* ev, bool)
5127 framepos_t f = adjusted_current_frame (ev);
5128 boost::shared_ptr<Region> r = _region_view->region ();
5129 f = max (f, r->position ());
5130 f = min (f, r->last_frame ());
5132 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5133 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5134 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5135 _cumulative_dx = dxu;
5139 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5141 if (!movement_occurred) {
5145 boost::shared_ptr<Region> r (_region_view->region ());
5146 framepos_t f = adjusted_current_frame (ev);
5147 f = max (f, r->position ());
5148 f = min (f, r->last_frame ());
5150 _region_view->move_patch_change (
5152 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5157 PatchChangeDrag::aborted (bool)
5159 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5163 PatchChangeDrag::setup_pointer_frame_offset ()
5165 boost::shared_ptr<Region> region = _region_view->region ();
5166 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5169 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5170 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5177 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5179 _region_view->update_drag_selection (
5181 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5185 MidiRubberbandSelectDrag::deselect_things ()
5190 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5191 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5194 _vertical_only = true;
5198 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5200 double const y = _region_view->midi_view()->y_position ();
5202 y1 = max (0.0, y1 - y);
5203 y2 = max (0.0, y2 - y);
5205 _region_view->update_vertical_drag_selection (
5208 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5213 MidiVerticalSelectDrag::deselect_things ()
5218 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5219 : RubberbandSelectDrag (e, i)
5225 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5227 if (drag_in_progress) {
5228 /* We just want to select things at the end of the drag, not during it */
5232 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5234 _editor->begin_reversible_selection_op (_("rubberband selection"));
5236 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5238 _editor->commit_reversible_selection_op ();
5242 EditorRubberbandSelectDrag::deselect_things ()
5244 _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)"));
5246 _editor->selection->clear_tracks();
5247 _editor->selection->clear_regions();
5248 _editor->selection->clear_points ();
5249 _editor->selection->clear_lines ();
5250 _editor->selection->clear_midi_notes ();
5252 _editor->commit_reversible_selection_op();
5255 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5260 _note[0] = _note[1] = 0;
5263 NoteCreateDrag::~NoteCreateDrag ()
5269 NoteCreateDrag::grid_frames (framepos_t t) const
5272 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5274 grid_beats = Evoral::Beats(1);
5277 return _region_view->region_beats_to_region_frames (grid_beats);
5281 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5283 Drag::start_grab (event, cursor);
5285 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5287 framepos_t pf = _drags->current_pointer_frame ();
5288 framecnt_t const g = grid_frames (pf);
5290 /* Hack so that we always snap to the note that we are over, instead of snapping
5291 to the next one if we're more than halfway through the one we're over.
5293 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5297 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5298 _note[1] = _note[0];
5300 MidiStreamView* sv = _region_view->midi_stream_view ();
5301 double const x = _editor->sample_to_pixel (_note[0]);
5302 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5304 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5305 _drag_rect->set_outline_all ();
5306 _drag_rect->set_outline_color (0xffffff99);
5307 _drag_rect->set_fill_color (0xffffff66);
5311 NoteCreateDrag::motion (GdkEvent* event, bool)
5313 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5314 double const x0 = _editor->sample_to_pixel (_note[0]);
5315 double const x1 = _editor->sample_to_pixel (_note[1]);
5316 _drag_rect->set_x0 (std::min(x0, x1));
5317 _drag_rect->set_x1 (std::max(x0, x1));
5321 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5323 if (!had_movement) {
5327 framepos_t const start = min (_note[0], _note[1]);
5328 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5330 framecnt_t const g = grid_frames (start);
5331 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5333 if (_editor->snap_mode() == SnapNormal && length < g) {
5337 Evoral::Beats length_beats = max (
5338 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5340 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5344 NoteCreateDrag::y_to_region (double y) const
5347 _region_view->get_canvas_group()->canvas_to_item (x, y);
5352 NoteCreateDrag::aborted (bool)
5357 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5362 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5366 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5368 Drag::start_grab (event, cursor);
5372 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5378 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5381 distance = _drags->current_pointer_x() - grab_x();
5382 len = ar->fade_in()->back()->when;
5384 distance = grab_x() - _drags->current_pointer_x();
5385 len = ar->fade_out()->back()->when;
5388 /* how long should it be ? */
5390 new_length = len + _editor->pixel_to_sample (distance);
5392 /* now check with the region that this is legal */
5394 new_length = ar->verify_xfade_bounds (new_length, start);
5397 arv->reset_fade_in_shape_width (ar, new_length);
5399 arv->reset_fade_out_shape_width (ar, new_length);
5404 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5410 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5413 distance = _drags->current_pointer_x() - grab_x();
5414 len = ar->fade_in()->back()->when;
5416 distance = grab_x() - _drags->current_pointer_x();
5417 len = ar->fade_out()->back()->when;
5420 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5422 _editor->begin_reversible_command ("xfade trim");
5423 ar->playlist()->clear_owned_changes ();
5426 ar->set_fade_in_length (new_length);
5428 ar->set_fade_out_length (new_length);
5431 /* Adjusting the xfade may affect other regions in the playlist, so we need
5432 to get undo Commands from the whole playlist rather than just the
5436 vector<Command*> cmds;
5437 ar->playlist()->rdiff (cmds);
5438 _editor->session()->add_commands (cmds);
5439 _editor->commit_reversible_command ();
5444 CrossfadeEdgeDrag::aborted (bool)
5447 arv->redraw_start_xfade ();
5449 arv->redraw_end_xfade ();
5453 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5454 : Drag (e, item, true)
5455 , line (new EditorCursor (*e))
5457 line->set_position (pos);
5461 RegionCutDrag::~RegionCutDrag ()
5467 RegionCutDrag::motion (GdkEvent*, bool)
5469 framepos_t where = _drags->current_pointer_frame();
5470 _editor->snap_to (where);
5472 line->set_position (where);
5476 RegionCutDrag::finished (GdkEvent*, bool)
5478 _editor->get_track_canvas()->canvas()->re_enter();
5480 framepos_t pos = _drags->current_pointer_frame();
5484 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5490 _editor->split_regions_at (pos, rs);
5494 RegionCutDrag::aborted (bool)