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/dB.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/operations.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
46 #include "ardour_ui.h"
47 #include "gui_thread.h"
48 #include "control_point.h"
50 #include "region_gain_line.h"
51 #include "editor_drag.h"
52 #include "audio_time_axis.h"
53 #include "midi_time_axis.h"
54 #include "selection.h"
55 #include "midi_selection.h"
56 #include "automation_time_axis.h"
58 #include "editor_cursors.h"
59 #include "mouse_cursors.h"
60 #include "note_base.h"
61 #include "patch_change.h"
62 #include "verbose_cursor.h"
65 using namespace ARDOUR;
68 using namespace Gtkmm2ext;
69 using namespace Editing;
70 using namespace ArdourCanvas;
72 using Gtkmm2ext::Keyboard;
74 double ControlPointDrag::_zero_gain_fraction = -1.0;
76 DragManager::DragManager (Editor* e)
79 , _current_pointer_frame (0)
83 DragManager::~DragManager ()
88 /** Call abort for each active drag */
94 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
99 if (!_drags.empty ()) {
100 _editor->set_follow_playhead (_old_follow_playhead, false);
109 DragManager::add (Drag* d)
111 d->set_manager (this);
112 _drags.push_back (d);
116 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
118 d->set_manager (this);
119 _drags.push_back (d);
124 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
126 /* Prevent follow playhead during the drag to be nice to the user */
127 _old_follow_playhead = _editor->follow_playhead ();
128 _editor->set_follow_playhead (false);
130 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
132 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
133 (*i)->start_grab (e, c);
137 /** Call end_grab for each active drag.
138 * @return true if any drag reported movement having occurred.
141 DragManager::end_grab (GdkEvent* e)
146 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
147 bool const t = (*i)->end_grab (e);
158 _editor->set_follow_playhead (_old_follow_playhead, false);
164 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
168 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
170 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
171 bool const t = (*i)->motion_handler (e, from_autoscroll);
182 DragManager::have_item (ArdourCanvas::Item* i) const
184 list<Drag*>::const_iterator j = _drags.begin ();
185 while (j != _drags.end() && (*j)->item () != i) {
189 return j != _drags.end ();
192 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
195 , _pointer_frame_offset (0)
196 , _move_threshold_passed (false)
197 , _raw_grab_frame (0)
199 , _last_pointer_frame (0)
205 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
218 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
220 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
222 if (Keyboard::is_button2_event (&event->button)) {
223 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
224 _y_constrained = true;
225 _x_constrained = false;
227 _y_constrained = false;
228 _x_constrained = true;
231 _x_constrained = false;
232 _y_constrained = false;
235 _raw_grab_frame = _editor->canvas_event_frame (event, &_grab_x, &_grab_y);
236 setup_pointer_frame_offset ();
237 _grab_frame = adjusted_frame (_raw_grab_frame, event);
238 _last_pointer_frame = _grab_frame;
239 _last_pointer_x = _grab_x;
240 _last_pointer_y = _grab_y;
246 /* CAIROCANVAS need a variant here that passes *cursor */
251 if (_editor->session() && _editor->session()->transport_rolling()) {
254 _was_rolling = false;
257 switch (_editor->snap_type()) {
258 case SnapToRegionStart:
259 case SnapToRegionEnd:
260 case SnapToRegionSync:
261 case SnapToRegionBoundary:
262 _editor->build_region_boundary_cache ();
269 /** Call to end a drag `successfully'. Ungrabs item and calls
270 * subclass' finished() method.
272 * @param event GDK event, or 0.
273 * @return true if some movement occurred, otherwise false.
276 Drag::end_grab (GdkEvent* event)
278 _editor->stop_canvas_autoscroll ();
282 finished (event, _move_threshold_passed);
284 _editor->verbose_cursor()->hide ();
286 return _move_threshold_passed;
290 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
294 if (f > _pointer_frame_offset) {
295 pos = f - _pointer_frame_offset;
299 _editor->snap_to_with_modifier (pos, event);
306 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
308 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
312 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
314 /* check to see if we have moved in any way that matters since the last motion event */
315 if (_move_threshold_passed &&
316 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
317 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
321 pair<framecnt_t, int> const threshold = move_threshold ();
323 bool const old_move_threshold_passed = _move_threshold_passed;
325 if (!from_autoscroll && !_move_threshold_passed) {
327 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
328 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
330 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
333 if (active (_editor->mouse_mode) && _move_threshold_passed) {
335 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
336 if (!from_autoscroll) {
337 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
338 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
339 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
342 motion (event, _move_threshold_passed != old_move_threshold_passed);
344 _last_pointer_x = _drags->current_pointer_x ();
345 _last_pointer_y = _drags->current_pointer_y ();
346 _last_pointer_frame = adjusted_current_frame (event);
354 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
362 aborted (_move_threshold_passed);
364 _editor->stop_canvas_autoscroll ();
365 _editor->verbose_cursor()->hide ();
369 Drag::show_verbose_cursor_time (framepos_t frame)
371 _editor->verbose_cursor()->set_time (
373 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
374 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
377 _editor->verbose_cursor()->show ();
381 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
383 _editor->verbose_cursor()->show (xoffset);
385 _editor->verbose_cursor()->set_duration (
387 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
388 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
393 Drag::show_verbose_cursor_text (string const & text)
395 _editor->verbose_cursor()->show ();
397 _editor->verbose_cursor()->set (
399 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
400 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
404 boost::shared_ptr<Region>
405 Drag::add_midi_region (MidiTimeAxisView* view)
407 if (_editor->session()) {
408 const TempoMap& map (_editor->session()->tempo_map());
409 framecnt_t pos = grab_frame();
410 const Meter& m = map.meter_at (pos);
411 /* not that the frame rate used here can be affected by pull up/down which
414 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
415 return view->add_region (grab_frame(), len, true);
418 return boost::shared_ptr<Region>();
421 struct EditorOrderTimeAxisViewSorter {
422 bool operator() (TimeAxisView* a, TimeAxisView* b) {
423 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
424 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
426 return ra->route()->order_key (EditorSort) < rb->route()->order_key (EditorSort);
430 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
434 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
436 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
437 as some of the regions we are dragging may be on such tracks.
440 TrackViewList track_views = _editor->track_views;
441 track_views.sort (EditorOrderTimeAxisViewSorter ());
443 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
444 _time_axis_views.push_back (*i);
446 TimeAxisView::Children children_list = (*i)->get_child_list ();
447 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
448 _time_axis_views.push_back (j->get());
452 /* the list of views can be empty at this point if this is a region list-insert drag
455 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
456 _views.push_back (DraggingView (*i, this));
459 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
463 RegionDrag::region_going_away (RegionView* v)
465 list<DraggingView>::iterator i = _views.begin ();
466 while (i != _views.end() && i->view != v) {
470 if (i != _views.end()) {
475 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
476 * or -1 if it is not found.
479 RegionDrag::find_time_axis_view (TimeAxisView* t) const
482 int const N = _time_axis_views.size ();
483 while (i < N && _time_axis_views[i] != t) {
494 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
495 : RegionDrag (e, i, p, v),
499 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
503 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
505 Drag::start_grab (event, cursor);
507 show_verbose_cursor_time (_last_frame_position);
509 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
510 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
511 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
515 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
517 /* compute the amount of pointer motion in frames, and where
518 the region would be if we moved it by that much.
520 *pending_region_position = adjusted_current_frame (event);
522 framepos_t sync_frame;
523 framecnt_t sync_offset;
526 sync_offset = _primary->region()->sync_offset (sync_dir);
528 /* we don't handle a sync point that lies before zero.
530 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
532 sync_frame = *pending_region_position + (sync_dir*sync_offset);
534 _editor->snap_to_with_modifier (sync_frame, event);
536 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
539 *pending_region_position = _last_frame_position;
542 if (*pending_region_position > max_framepos - _primary->region()->length()) {
543 *pending_region_position = _last_frame_position;
548 /* in locked edit mode, reverse the usual meaning of _x_constrained */
549 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
551 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
553 /* x movement since last time (in pixels) */
554 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
556 /* total x movement */
557 framecnt_t total_dx = *pending_region_position;
558 if (regions_came_from_canvas()) {
559 total_dx = total_dx - grab_frame ();
562 /* check that no regions have gone off the start of the session */
563 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
564 if ((i->view->region()->position() + total_dx) < 0) {
566 *pending_region_position = _last_frame_position;
571 _last_frame_position = *pending_region_position;
578 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
580 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
581 int const n = i->time_axis_view + delta_track;
582 if (n < 0 || n >= int (_time_axis_views.size())) {
583 /* off the top or bottom track */
587 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
588 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
589 /* not a track, or the wrong type */
593 double const l = i->layer + delta_layer;
595 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
596 mode to allow the user to place a region below another on layer 0.
598 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
599 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
600 If it has, the layers will be munged later anyway, so it's ok.
606 /* all regions being dragged are ok with this change */
611 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
613 assert (!_views.empty ());
615 /* Find the TimeAxisView that the pointer is now over */
616 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
618 if (first_move && tv.first->view()->layer_display() == Stacked) {
619 tv.first->view()->set_layer_display (Expanded);
622 /* Bail early if we're not over a track */
623 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
624 if (!rtv || !rtv->is_track()) {
625 _editor->verbose_cursor()->hide ();
629 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
631 /* Here's the current pointer position in terms of time axis view and layer */
632 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
633 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
635 /* Work out the change in x */
636 framepos_t pending_region_position;
637 double const x_delta = compute_x_delta (event, &pending_region_position);
639 /* Work out the change in y */
641 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
642 double delta_layer = current_pointer_layer - _last_pointer_layer;
644 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
645 /* this y movement is not allowed, so do no y movement this time */
646 delta_time_axis_view = 0;
650 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
651 /* haven't reached next snap point, and we're not switching
652 trackviews nor layers. nothing to do.
657 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
659 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
661 RegionView* rv = i->view;
663 if (rv->region()->locked() || rv->region()->video_locked()) {
671 /* Reparent to a non scrolling group so that we can keep the
672 region selection above all time axis views.
673 Reparenting means that we will have to move the region view
674 within its new parent, as the two parent groups have different coordinates.
677 ArdourCanvas::Group* rvg = rv->get_canvas_group();
678 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
680 rv->get_canvas_group()->reparent (_editor->_region_motion_group);
682 rv->fake_set_opaque (true);
683 rvg->set_position (rv_canvas_offset);
686 /* If we have moved tracks, we'll fudge the layer delta so that the
687 region gets moved back onto layer 0 on its new track; this avoids
688 confusion when dragging regions from non-zero layers onto different
691 double this_delta_layer = delta_layer;
692 if (delta_time_axis_view != 0) {
693 this_delta_layer = - i->layer;
696 /* The TimeAxisView that this region is now on */
697 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
699 /* Ensure it is moved from stacked -> expanded if appropriate */
700 if (tv->view()->layer_display() == Stacked) {
701 tv->view()->set_layer_display (Expanded);
704 /* We're only allowed to go -ve in layer on Expanded views */
705 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
706 this_delta_layer = - i->layer;
710 rv->set_height (tv->view()->child_height ());
712 /* Update show/hidden status as the region view may have come from a hidden track,
713 or have moved to one.
716 rv->get_canvas_group()->hide ();
718 rv->get_canvas_group()->show ();
721 /* Update the DraggingView */
722 i->time_axis_view += delta_time_axis_view;
723 i->layer += this_delta_layer;
726 _editor->mouse_brush_insert_region (rv, pending_region_position);
731 /* Get the y coordinate of the top of the track that this region is now on */
732 tv->canvas_display()->item_to_canvas (x, y);
734 /* And adjust for the layer that it should be on */
735 StreamView* cv = tv->view ();
736 switch (cv->layer_display ()) {
740 y += (cv->layers() - i->layer - 1) * cv->child_height ();
743 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
747 /* Now move the region view */
748 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
751 } /* foreach region */
753 _total_x_delta += x_delta;
755 if (x_delta != 0 && !_brushing) {
756 show_verbose_cursor_time (_last_frame_position);
759 _last_pointer_time_axis_view += delta_time_axis_view;
760 _last_pointer_layer += delta_layer;
764 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
766 if (_copy && first_move) {
768 /* duplicate the regionview(s) and region(s) */
770 list<DraggingView> new_regionviews;
772 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
774 RegionView* rv = i->view;
775 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
776 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
778 const boost::shared_ptr<const Region> original = rv->region();
779 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
780 region_copy->set_position (original->position());
784 boost::shared_ptr<AudioRegion> audioregion_copy
785 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
787 nrv = new AudioRegionView (*arv, audioregion_copy);
789 boost::shared_ptr<MidiRegion> midiregion_copy
790 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
791 nrv = new MidiRegionView (*mrv, midiregion_copy);
796 nrv->get_canvas_group()->show ();
797 new_regionviews.push_back (DraggingView (nrv, this));
799 /* swap _primary to the copy */
801 if (rv == _primary) {
805 /* ..and deselect the one we copied */
807 rv->set_selected (false);
810 if (!new_regionviews.empty()) {
812 /* reflect the fact that we are dragging the copies */
814 _views = new_regionviews;
816 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
820 RegionMotionDrag::motion (event, first_move);
824 RegionMotionDrag::finished (GdkEvent *, bool)
826 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
831 if ((*i)->view()->layer_display() == Expanded) {
832 (*i)->view()->set_layer_display (Stacked);
838 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
840 RegionMotionDrag::finished (ev, movement_occurred);
842 if (!movement_occurred) {
847 /* reverse this here so that we have the correct logic to finalize
851 if (Config->get_edit_mode() == Lock) {
852 _x_constrained = !_x_constrained;
855 assert (!_views.empty ());
857 /* We might have hidden region views so that they weren't visible during the drag
858 (when they have been reparented). Now everything can be shown again, as region
859 views are back in their track parent groups.
861 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
862 i->view->get_canvas_group()->show ();
865 bool const changed_position = (_last_frame_position != _primary->region()->position());
866 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
867 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
887 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
891 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
893 RegionSelection new_views;
894 PlaylistSet modified_playlists;
895 list<RegionView*> views_to_delete;
898 /* all changes were made during motion event handlers */
900 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
904 _editor->commit_reversible_command ();
908 if (_x_constrained) {
909 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
911 _editor->begin_reversible_command (Operations::region_copy);
914 /* insert the regions into their new playlists */
915 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
917 if (i->view->region()->locked() || i->view->region()->video_locked()) {
923 if (changed_position && !_x_constrained) {
924 where = i->view->region()->position() - drag_delta;
926 where = i->view->region()->position();
929 RegionView* new_view = insert_region_into_playlist (
930 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
937 new_views.push_back (new_view);
939 /* we don't need the copied RegionView any more */
940 views_to_delete.push_back (i->view);
943 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
944 because when views are deleted they are automagically removed from _views, which messes
947 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
951 /* If we've created new regions either by copying or moving
952 to a new track, we want to replace the old selection with the new ones
955 if (new_views.size() > 0) {
956 _editor->selection->set (new_views);
959 /* write commands for the accumulated diffs for all our modified playlists */
960 add_stateful_diff_commands_for_playlists (modified_playlists);
962 _editor->commit_reversible_command ();
966 RegionMoveDrag::finished_no_copy (
967 bool const changed_position,
968 bool const changed_tracks,
969 framecnt_t const drag_delta
972 RegionSelection new_views;
973 PlaylistSet modified_playlists;
974 PlaylistSet frozen_playlists;
975 set<RouteTimeAxisView*> views_to_update;
978 /* all changes were made during motion event handlers */
979 _editor->commit_reversible_command ();
983 if (_x_constrained) {
984 _editor->begin_reversible_command (_("fixed time region drag"));
986 _editor->begin_reversible_command (Operations::region_drag);
989 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
991 RegionView* rv = i->view;
993 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
994 double const dest_layer = i->layer;
996 if (rv->region()->locked() || rv->region()->video_locked()) {
1001 views_to_update.insert (dest_rtv);
1005 if (changed_position && !_x_constrained) {
1006 where = rv->region()->position() - drag_delta;
1008 where = rv->region()->position();
1011 if (changed_tracks) {
1013 /* insert into new playlist */
1015 RegionView* new_view = insert_region_into_playlist (
1016 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1019 if (new_view == 0) {
1024 new_views.push_back (new_view);
1026 /* remove from old playlist */
1028 /* the region that used to be in the old playlist is not
1029 moved to the new one - we use a copy of it. as a result,
1030 any existing editor for the region should no longer be
1033 rv->hide_region_editor();
1034 rv->fake_set_opaque (false);
1036 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1040 rv->region()->clear_changes ();
1043 motion on the same track. plonk the previously reparented region
1044 back to its original canvas group (its streamview).
1045 No need to do anything for copies as they are fake regions which will be deleted.
1048 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1049 rv->get_canvas_group()->set_y_position (i->initial_y);
1052 /* just change the model */
1054 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1056 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1057 playlist->set_layer (rv->region(), dest_layer);
1060 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1062 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1065 playlist->freeze ();
1068 /* this movement may result in a crossfade being modified, so we need to get undo
1069 data from the playlist as well as the region.
1072 r = modified_playlists.insert (playlist);
1074 playlist->clear_changes ();
1077 rv->region()->set_position (where);
1079 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1082 if (changed_tracks) {
1084 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1085 was selected in all of them, then removing it from a playlist will have removed all
1086 trace of it from _views (i.e. there were N regions selected, we removed 1,
1087 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1088 corresponding regionview, and _views is now empty).
1090 This could have invalidated any and all iterators into _views.
1092 The heuristic we use here is: if the region selection is empty, break out of the loop
1093 here. if the region selection is not empty, then restart the loop because we know that
1094 we must have removed at least the region(view) we've just been working on as well as any
1095 that we processed on previous iterations.
1097 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1098 we can just iterate.
1102 if (_views.empty()) {
1113 /* If we've created new regions either by copying or moving
1114 to a new track, we want to replace the old selection with the new ones
1117 if (new_views.size() > 0) {
1118 _editor->selection->set (new_views);
1121 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1125 /* write commands for the accumulated diffs for all our modified playlists */
1126 add_stateful_diff_commands_for_playlists (modified_playlists);
1128 _editor->commit_reversible_command ();
1130 /* We have futzed with the layering of canvas items on our streamviews.
1131 If any region changed layer, this will have resulted in the stream
1132 views being asked to set up their region views, and all will be well.
1133 If not, we might now have badly-ordered region views. Ask the StreamViews
1134 involved to sort themselves out, just in case.
1137 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1138 (*i)->view()->playlist_layered ((*i)->track ());
1142 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1143 * @param region Region to remove.
1144 * @param playlist playlist To remove from.
1145 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1146 * that clear_changes () is only called once per playlist.
1149 RegionMoveDrag::remove_region_from_playlist (
1150 boost::shared_ptr<Region> region,
1151 boost::shared_ptr<Playlist> playlist,
1152 PlaylistSet& modified_playlists
1155 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1158 playlist->clear_changes ();
1161 playlist->remove_region (region);
1165 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1166 * clearing the playlist's diff history first if necessary.
1167 * @param region Region to insert.
1168 * @param dest_rtv Destination RouteTimeAxisView.
1169 * @param dest_layer Destination layer.
1170 * @param where Destination position.
1171 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1172 * that clear_changes () is only called once per playlist.
1173 * @return New RegionView, or 0 if no insert was performed.
1176 RegionMoveDrag::insert_region_into_playlist (
1177 boost::shared_ptr<Region> region,
1178 RouteTimeAxisView* dest_rtv,
1181 PlaylistSet& modified_playlists
1184 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1185 if (!dest_playlist) {
1189 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1190 _new_region_view = 0;
1191 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1193 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1194 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1196 dest_playlist->clear_changes ();
1199 dest_playlist->add_region (region, where);
1201 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1202 dest_playlist->set_layer (region, dest_layer);
1207 assert (_new_region_view);
1209 return _new_region_view;
1213 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1215 _new_region_view = rv;
1219 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1221 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1222 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1224 _editor->session()->add_command (c);
1233 RegionMoveDrag::aborted (bool movement_occurred)
1237 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1244 RegionMotionDrag::aborted (movement_occurred);
1249 RegionMotionDrag::aborted (bool)
1251 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1252 if ((*i)->view()->layer_display() == Expanded) {
1253 (*i)->view()->set_layer_display (Stacked);
1257 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1258 RegionView* rv = i->view;
1259 TimeAxisView* tv = &(rv->get_time_axis_view ());
1260 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1262 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1263 rv->get_canvas_group()->set_y_position (0);
1265 rv->fake_set_opaque (false);
1266 rv->move (-_total_x_delta, 0);
1267 rv->set_height (rtv->view()->child_height ());
1271 /** @param b true to brush, otherwise false.
1272 * @param c true to make copies of the regions being moved, otherwise false.
1274 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1275 : RegionMotionDrag (e, i, p, v, b),
1278 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1281 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1282 if (rtv && rtv->is_track()) {
1283 speed = rtv->track()->speed ();
1286 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1290 RegionMoveDrag::setup_pointer_frame_offset ()
1292 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1295 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1296 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1298 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1300 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1301 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1303 _primary = v->view()->create_region_view (r, false, false);
1305 _primary->get_canvas_group()->show ();
1306 _primary->set_position (pos, 0);
1307 _views.push_back (DraggingView (_primary, this));
1309 _last_frame_position = pos;
1311 _item = _primary->get_canvas_group ();
1315 RegionInsertDrag::finished (GdkEvent *, bool)
1317 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1319 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1320 _primary->get_canvas_group()->set_y_position (0);
1322 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1324 _editor->begin_reversible_command (Operations::insert_region);
1325 playlist->clear_changes ();
1326 playlist->add_region (_primary->region (), _last_frame_position);
1327 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1328 _editor->commit_reversible_command ();
1336 RegionInsertDrag::aborted (bool)
1343 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1344 : RegionMoveDrag (e, i, p, v, false, false)
1346 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1349 struct RegionSelectionByPosition {
1350 bool operator() (RegionView*a, RegionView* b) {
1351 return a->region()->position () < b->region()->position();
1356 RegionSpliceDrag::motion (GdkEvent* event, bool)
1358 /* Which trackview is this ? */
1360 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1361 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1363 /* The region motion is only processed if the pointer is over
1367 if (!tv || !tv->is_track()) {
1368 /* To make sure we hide the verbose canvas cursor when the mouse is
1369 not held over and audiotrack.
1371 _editor->verbose_cursor()->hide ();
1377 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1383 RegionSelection copy (_editor->selection->regions);
1385 RegionSelectionByPosition cmp;
1388 framepos_t const pf = adjusted_current_frame (event);
1390 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1392 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1398 boost::shared_ptr<Playlist> playlist;
1400 if ((playlist = atv->playlist()) == 0) {
1404 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1409 if (pf < (*i)->region()->last_frame() + 1) {
1413 if (pf > (*i)->region()->first_frame()) {
1419 playlist->shuffle ((*i)->region(), dir);
1424 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1426 RegionMoveDrag::finished (event, movement_occurred);
1430 RegionSpliceDrag::aborted (bool)
1435 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1437 _view (dynamic_cast<MidiTimeAxisView*> (v))
1439 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1445 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1448 _region = add_midi_region (_view);
1449 _view->playlist()->freeze ();
1452 framepos_t const f = adjusted_current_frame (event);
1453 if (f < grab_frame()) {
1454 _region->set_position (f);
1457 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1458 so that if this region is duplicated, its duplicate starts on
1459 a snap point rather than 1 frame after a snap point. Otherwise things get
1460 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1461 place snapped notes at the start of the region.
1464 framecnt_t const len = (framecnt_t) fabs (f - grab_frame () - 1);
1465 _region->set_length (len < 1 ? 1 : len);
1471 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1473 if (!movement_occurred) {
1474 add_midi_region (_view);
1476 _view->playlist()->thaw ();
1481 RegionCreateDrag::aborted (bool)
1484 _view->playlist()->thaw ();
1490 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1494 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1498 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1500 Gdk::Cursor* cursor;
1501 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1503 float x_fraction = cnote->mouse_x_fraction ();
1505 if (x_fraction > 0.0 && x_fraction < 0.25) {
1506 cursor = _editor->cursors()->left_side_trim;
1508 cursor = _editor->cursors()->right_side_trim;
1511 Drag::start_grab (event, cursor);
1513 region = &cnote->region_view();
1515 double const region_start = region->get_position_pixels();
1516 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1518 if (grab_x() <= middle_point) {
1519 cursor = _editor->cursors()->left_side_trim;
1522 cursor = _editor->cursors()->right_side_trim;
1528 if (event->motion.state & Keyboard::PrimaryModifier) {
1534 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1536 if (ms.size() > 1) {
1537 /* has to be relative, may make no sense otherwise */
1541 /* select this note; if it is already selected, preserve the existing selection,
1542 otherwise make this note the only one selected.
1544 region->note_selected (cnote, cnote->selected ());
1546 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1547 MidiRegionSelection::iterator next;
1550 (*r)->begin_resizing (at_front);
1556 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1558 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1559 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1560 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1562 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1567 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1569 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1570 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1571 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1573 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1578 NoteResizeDrag::aborted (bool)
1580 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1581 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1582 (*r)->abort_resizing ();
1586 AVDraggingView::AVDraggingView (RegionView* v)
1589 initial_position = v->region()->position ();
1592 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1595 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1598 TrackViewList empty;
1600 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1601 std::list<RegionView*> views = rs.by_layer();
1603 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1604 RegionView* rv = (*i);
1605 if (!rv->region()->video_locked()) {
1608 _views.push_back (AVDraggingView (rv));
1613 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1615 Drag::start_grab (event);
1616 if (_editor->session() == 0) {
1620 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1621 _max_backwards_drag = (
1622 ARDOUR_UI::instance()->video_timeline->get_duration()
1623 + ARDOUR_UI::instance()->video_timeline->get_offset()
1624 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1627 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1628 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1629 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1632 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1635 Timecode::Time timecode;
1636 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1637 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);
1638 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1639 _editor->verbose_cursor()->show ();
1643 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1645 if (_editor->session() == 0) {
1648 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1652 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1653 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(dt);
1655 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1656 dt = - _max_backwards_drag;
1659 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1660 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1662 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1663 RegionView* rv = i->view;
1664 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1667 rv->fake_set_opaque (true);
1668 rv->region()->clear_changes ();
1669 rv->region()->suspend_property_changes();
1671 rv->region()->set_position(i->initial_position + dt);
1672 rv->region_changed(ARDOUR::Properties::position);
1675 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1676 Timecode::Time timecode;
1677 Timecode::Time timediff;
1679 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1680 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1681 snprintf (buf, sizeof (buf),
1682 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1683 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1684 , _("Video Start:"),
1685 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1687 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1689 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1690 _editor->verbose_cursor()->show ();
1694 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1696 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1700 if (!movement_occurred || ! _editor->session()) {
1704 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1706 _editor->begin_reversible_command (_("Move Video"));
1708 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1709 ARDOUR_UI::instance()->video_timeline->save_undo();
1710 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1711 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1713 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1714 i->view->drag_end();
1715 i->view->fake_set_opaque (false);
1716 i->view->region()->resume_property_changes ();
1718 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1721 _editor->session()->maybe_update_session_range(
1722 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1723 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1727 _editor->commit_reversible_command ();
1731 VideoTimeLineDrag::aborted (bool)
1733 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1736 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1737 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1739 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1740 i->view->region()->resume_property_changes ();
1741 i->view->region()->set_position(i->initial_position);
1745 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1746 : RegionDrag (e, i, p, v)
1748 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1749 _preserve_fade_anchor = preserve_fade_anchor;
1753 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1756 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1757 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1759 if (tv && tv->is_track()) {
1760 speed = tv->track()->speed();
1763 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1764 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1765 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1767 framepos_t const pf = adjusted_current_frame (event);
1769 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1770 /* Move the contents of the region around without changing the region bounds */
1771 _operation = ContentsTrim;
1772 Drag::start_grab (event, _editor->cursors()->trimmer);
1774 /* These will get overridden for a point trim.*/
1775 if (pf < (region_start + region_length/2)) {
1776 /* closer to front */
1777 _operation = StartTrim;
1778 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1781 _operation = EndTrim;
1782 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1786 switch (_operation) {
1788 show_verbose_cursor_time (region_start);
1789 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1790 i->view->trim_front_starting ();
1794 show_verbose_cursor_time (region_end);
1797 show_verbose_cursor_time (pf);
1801 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1802 i->view->region()->suspend_property_changes ();
1807 TrimDrag::motion (GdkEvent* event, bool first_move)
1809 RegionView* rv = _primary;
1812 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1813 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1814 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1816 if (tv && tv->is_track()) {
1817 speed = tv->track()->speed();
1820 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1826 switch (_operation) {
1828 trim_type = "Region start trim";
1831 trim_type = "Region end trim";
1834 trim_type = "Region content trim";
1838 _editor->begin_reversible_command (trim_type);
1840 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1841 RegionView* rv = i->view;
1842 rv->fake_set_opaque (false);
1843 rv->enable_display (false);
1844 rv->region()->playlist()->clear_owned_changes ();
1846 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1849 arv->temporarily_hide_envelope ();
1853 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1854 insert_result = _editor->motion_frozen_playlists.insert (pl);
1856 if (insert_result.second) {
1862 bool non_overlap_trim = false;
1864 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1865 non_overlap_trim = true;
1868 switch (_operation) {
1870 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1871 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1872 if (changed && _preserve_fade_anchor) {
1873 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1878 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1879 distance = _drags->current_pointer_x() - grab_x();
1880 len = ar->fade_in()->back()->when;
1881 new_length = len - _editor->pixel_to_sample (distance);
1882 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1883 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1890 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1891 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1892 if (changed && _preserve_fade_anchor) {
1893 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1898 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1899 distance = grab_x() - _drags->current_pointer_x();
1900 len = ar->fade_out()->back()->when;
1901 new_length = len - _editor->pixel_to_sample (distance);
1902 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1903 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1911 bool swap_direction = false;
1913 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1914 swap_direction = true;
1917 framecnt_t frame_delta = 0;
1919 bool left_direction = false;
1920 if (last_pointer_frame() > adjusted_current_frame(event)) {
1921 left_direction = true;
1924 if (left_direction) {
1925 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1927 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1930 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1931 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1937 switch (_operation) {
1939 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1942 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1945 show_verbose_cursor_time (adjusted_current_frame (event));
1952 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1954 if (movement_occurred) {
1955 motion (event, false);
1957 if (_operation == StartTrim) {
1958 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1960 /* This must happen before the region's StatefulDiffCommand is created, as it may
1961 `correct' (ahem) the region's _start from being negative to being zero. It
1962 needs to be zero in the undo record.
1964 i->view->trim_front_ending ();
1966 if (_preserve_fade_anchor) {
1967 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1972 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1973 distance = _drags->current_pointer_x() - grab_x();
1974 len = ar->fade_in()->back()->when;
1975 new_length = len - _editor->pixel_to_sample (distance);
1976 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1977 ar->set_fade_in_length(new_length);
1981 } else if (_operation == EndTrim) {
1982 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1983 if (_preserve_fade_anchor) {
1984 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1989 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1990 distance = _drags->current_pointer_x() - grab_x();
1991 len = ar->fade_out()->back()->when;
1992 new_length = len - _editor->pixel_to_sample (distance);
1993 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1994 ar->set_fade_out_length(new_length);
2000 if (!_views.empty()) {
2001 if (_operation == StartTrim) {
2002 _editor->maybe_locate_with_edit_preroll(
2003 _views.begin()->view->region()->position());
2005 if (_operation == EndTrim) {
2006 _editor->maybe_locate_with_edit_preroll(
2007 _views.begin()->view->region()->position() +
2008 _views.begin()->view->region()->length());
2012 if (!_editor->selection->selected (_primary)) {
2013 _primary->thaw_after_trim ();
2016 set<boost::shared_ptr<Playlist> > diffed_playlists;
2018 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2019 i->view->thaw_after_trim ();
2020 i->view->enable_display (true);
2021 i->view->fake_set_opaque (true);
2023 /* Trimming one region may affect others on the playlist, so we need
2024 to get undo Commands from the whole playlist rather than just the
2025 region. Use diffed_playlists to make sure we don't diff a given
2026 playlist more than once.
2028 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2029 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2030 vector<Command*> cmds;
2032 _editor->session()->add_commands (cmds);
2033 diffed_playlists.insert (p);
2038 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2042 _editor->motion_frozen_playlists.clear ();
2043 _editor->commit_reversible_command();
2046 /* no mouse movement */
2047 _editor->point_trim (event, adjusted_current_frame (event));
2050 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2051 if (_operation == StartTrim) {
2052 i->view->trim_front_ending ();
2055 i->view->region()->resume_property_changes ();
2060 TrimDrag::aborted (bool movement_occurred)
2062 /* Our motion method is changing model state, so use the Undo system
2063 to cancel. Perhaps not ideal, as this will leave an Undo point
2064 behind which may be slightly odd from the user's point of view.
2069 if (movement_occurred) {
2073 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2074 i->view->region()->resume_property_changes ();
2079 TrimDrag::setup_pointer_frame_offset ()
2081 list<DraggingView>::iterator i = _views.begin ();
2082 while (i != _views.end() && i->view != _primary) {
2086 if (i == _views.end()) {
2090 switch (_operation) {
2092 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2095 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2102 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2106 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2107 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2112 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2114 Drag::start_grab (event, cursor);
2115 show_verbose_cursor_time (adjusted_current_frame(event));
2119 MeterMarkerDrag::setup_pointer_frame_offset ()
2121 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2125 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2129 // create a dummy marker for visual representation of moving the
2130 // section, because whether its a copy or not, we're going to
2131 // leave or lose the original marker (leave if its a copy; lose if its
2132 // not, because we'll remove it from the map).
2134 MeterSection section (_marker->meter());
2136 if (!section.movable()) {
2141 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2143 _marker = new MeterMarker (
2145 *_editor->meter_group,
2146 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2148 *new MeterSection (_marker->meter())
2151 /* use the new marker for the grab */
2152 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2155 TempoMap& map (_editor->session()->tempo_map());
2156 /* get current state */
2157 before_state = &map.get_state();
2158 /* remove the section while we drag it */
2159 map.remove_meter (section, true);
2163 framepos_t const pf = adjusted_current_frame (event);
2164 _marker->set_position (pf);
2165 show_verbose_cursor_time (pf);
2169 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2171 if (!movement_occurred) {
2175 motion (event, false);
2177 Timecode::BBT_Time when;
2179 TempoMap& map (_editor->session()->tempo_map());
2180 map.bbt_time (last_pointer_frame(), when);
2182 if (_copy == true) {
2183 _editor->begin_reversible_command (_("copy meter mark"));
2184 XMLNode &before = map.get_state();
2185 map.add_meter (_marker->meter(), when);
2186 XMLNode &after = map.get_state();
2187 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2188 _editor->commit_reversible_command ();
2191 _editor->begin_reversible_command (_("move meter mark"));
2193 /* we removed it before, so add it back now */
2195 map.add_meter (_marker->meter(), when);
2196 XMLNode &after = map.get_state();
2197 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2198 _editor->commit_reversible_command ();
2201 // delete the dummy marker we used for visual representation while moving.
2202 // a new visual marker will show up automatically.
2207 MeterMarkerDrag::aborted (bool moved)
2209 _marker->set_position (_marker->meter().frame ());
2212 TempoMap& map (_editor->session()->tempo_map());
2213 /* we removed it before, so add it back now */
2214 map.add_meter (_marker->meter(), _marker->meter().frame());
2215 // delete the dummy marker we used for visual representation while moving.
2216 // a new visual marker will show up automatically.
2221 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2225 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2227 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2232 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2234 Drag::start_grab (event, cursor);
2235 show_verbose_cursor_time (adjusted_current_frame (event));
2239 TempoMarkerDrag::setup_pointer_frame_offset ()
2241 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2245 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2249 // create a dummy marker for visual representation of moving the
2250 // section, because whether its a copy or not, we're going to
2251 // leave or lose the original marker (leave if its a copy; lose if its
2252 // not, because we'll remove it from the map).
2254 // create a dummy marker for visual representation of moving the copy.
2255 // The actual copying is not done before we reach the finish callback.
2258 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2260 TempoSection section (_marker->tempo());
2262 _marker = new TempoMarker (
2264 *_editor->tempo_group,
2265 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2267 *new TempoSection (_marker->tempo())
2270 /* use the new marker for the grab */
2271 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2274 TempoMap& map (_editor->session()->tempo_map());
2275 /* get current state */
2276 before_state = &map.get_state();
2277 /* remove the section while we drag it */
2278 map.remove_tempo (section, true);
2282 framepos_t const pf = adjusted_current_frame (event);
2283 _marker->set_position (pf);
2284 show_verbose_cursor_time (pf);
2288 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2290 if (!movement_occurred) {
2294 motion (event, false);
2296 TempoMap& map (_editor->session()->tempo_map());
2297 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2298 Timecode::BBT_Time when;
2300 map.bbt_time (beat_time, when);
2302 if (_copy == true) {
2303 _editor->begin_reversible_command (_("copy tempo mark"));
2304 XMLNode &before = map.get_state();
2305 map.add_tempo (_marker->tempo(), when);
2306 XMLNode &after = map.get_state();
2307 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2308 _editor->commit_reversible_command ();
2311 _editor->begin_reversible_command (_("move tempo mark"));
2312 /* we removed it before, so add it back now */
2313 map.add_tempo (_marker->tempo(), when);
2314 XMLNode &after = map.get_state();
2315 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2316 _editor->commit_reversible_command ();
2319 // delete the dummy marker we used for visual representation while moving.
2320 // a new visual marker will show up automatically.
2325 TempoMarkerDrag::aborted (bool moved)
2327 _marker->set_position (_marker->tempo().frame());
2329 TempoMap& map (_editor->session()->tempo_map());
2330 /* we removed it before, so add it back now */
2331 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2332 // delete the dummy marker we used for visual representation while moving.
2333 // a new visual marker will show up automatically.
2338 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2339 : Drag (e, &c.time_bar_canvas_item())
2343 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2346 /** Do all the things we do when dragging the playhead to make it look as though
2347 * we have located, without actually doing the locate (because that would cause
2348 * the diskstream buffers to be refilled, which is too slow).
2351 CursorDrag::fake_locate (framepos_t t)
2353 _editor->playhead_cursor->set_position (t);
2355 Session* s = _editor->session ();
2356 if (s->timecode_transmission_suspended ()) {
2357 framepos_t const f = _editor->playhead_cursor->current_frame ();
2358 s->send_mmc_locate (f);
2359 s->send_full_time_code (f);
2362 show_verbose_cursor_time (t);
2363 _editor->UpdateAllTransportClocks (t);
2367 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2369 Drag::start_grab (event, c);
2371 _grab_zoom = _editor->samples_per_pixel;
2373 framepos_t where = _editor->canvas_event_frame (event, 0, 0);
2374 _editor->snap_to_with_modifier (where, event);
2376 _editor->_dragging_playhead = true;
2378 Session* s = _editor->session ();
2380 /* grab the track canvas item as well */
2382 _cursor.track_canvas_item().grab();
2385 if (_was_rolling && _stop) {
2389 if (s->is_auditioning()) {
2390 s->cancel_audition ();
2394 if (AudioEngine::instance()->connected()) {
2396 /* do this only if we're the engine is connected
2397 * because otherwise this request will never be
2398 * serviced and we'll busy wait forever. likewise,
2399 * notice if we are disconnected while waiting for the
2400 * request to be serviced.
2403 s->request_suspend_timecode_transmission ();
2404 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2405 /* twiddle our thumbs */
2410 fake_locate (where);
2414 CursorDrag::motion (GdkEvent* event, bool)
2416 framepos_t const adjusted_frame = adjusted_current_frame (event);
2417 if (adjusted_frame != last_pointer_frame()) {
2418 fake_locate (adjusted_frame);
2423 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2425 _editor->_dragging_playhead = false;
2427 _cursor.track_canvas_item().ungrab();
2429 if (!movement_occurred && _stop) {
2433 motion (event, false);
2435 Session* s = _editor->session ();
2437 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2438 _editor->_pending_locate_request = true;
2439 s->request_resume_timecode_transmission ();
2444 CursorDrag::aborted (bool)
2446 _cursor.track_canvas_item().ungrab();
2448 if (_editor->_dragging_playhead) {
2449 _editor->session()->request_resume_timecode_transmission ();
2450 _editor->_dragging_playhead = false;
2453 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2456 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2457 : RegionDrag (e, i, p, v)
2459 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2463 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2465 Drag::start_grab (event, cursor);
2467 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2468 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2470 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2474 FadeInDrag::setup_pointer_frame_offset ()
2476 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2477 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2478 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2482 FadeInDrag::motion (GdkEvent* event, bool)
2484 framecnt_t fade_length;
2486 framepos_t const pos = adjusted_current_frame (event);
2488 boost::shared_ptr<Region> region = _primary->region ();
2490 if (pos < (region->position() + 64)) {
2491 fade_length = 64; // this should be a minimum defined somewhere
2492 } else if (pos > region->last_frame()) {
2493 fade_length = region->length();
2495 fade_length = pos - region->position();
2498 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2500 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2506 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2509 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2513 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2515 if (!movement_occurred) {
2519 framecnt_t fade_length;
2521 framepos_t const pos = adjusted_current_frame (event);
2523 boost::shared_ptr<Region> region = _primary->region ();
2525 if (pos < (region->position() + 64)) {
2526 fade_length = 64; // this should be a minimum defined somewhere
2527 } else if (pos > region->last_frame()) {
2528 fade_length = region->length();
2530 fade_length = pos - region->position();
2533 _editor->begin_reversible_command (_("change fade in length"));
2535 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2537 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2543 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2544 XMLNode &before = alist->get_state();
2546 tmp->audio_region()->set_fade_in_length (fade_length);
2547 tmp->audio_region()->set_fade_in_active (true);
2549 XMLNode &after = alist->get_state();
2550 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2553 _editor->commit_reversible_command ();
2557 FadeInDrag::aborted (bool)
2559 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2560 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2566 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2570 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2571 : RegionDrag (e, i, p, v)
2573 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2577 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2579 Drag::start_grab (event, cursor);
2581 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2582 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2584 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2588 FadeOutDrag::setup_pointer_frame_offset ()
2590 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2591 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2592 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2596 FadeOutDrag::motion (GdkEvent* event, bool)
2598 framecnt_t fade_length;
2600 framepos_t const pos = adjusted_current_frame (event);
2602 boost::shared_ptr<Region> region = _primary->region ();
2604 if (pos > (region->last_frame() - 64)) {
2605 fade_length = 64; // this should really be a minimum fade defined somewhere
2607 else if (pos < region->position()) {
2608 fade_length = region->length();
2611 fade_length = region->last_frame() - pos;
2614 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2616 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2622 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2625 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2629 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2631 if (!movement_occurred) {
2635 framecnt_t fade_length;
2637 framepos_t const pos = adjusted_current_frame (event);
2639 boost::shared_ptr<Region> region = _primary->region ();
2641 if (pos > (region->last_frame() - 64)) {
2642 fade_length = 64; // this should really be a minimum fade defined somewhere
2644 else if (pos < region->position()) {
2645 fade_length = region->length();
2648 fade_length = region->last_frame() - pos;
2651 _editor->begin_reversible_command (_("change fade out length"));
2653 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2655 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2661 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2662 XMLNode &before = alist->get_state();
2664 tmp->audio_region()->set_fade_out_length (fade_length);
2665 tmp->audio_region()->set_fade_out_active (true);
2667 XMLNode &after = alist->get_state();
2668 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2671 _editor->commit_reversible_command ();
2675 FadeOutDrag::aborted (bool)
2677 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2678 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2684 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2688 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2691 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2693 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2696 _points.push_back (ArdourCanvas::Duple (0, 0));
2697 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2700 MarkerDrag::~MarkerDrag ()
2702 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2707 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2709 location = new Location (*l);
2710 markers.push_back (m);
2715 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2717 Drag::start_grab (event, cursor);
2721 Location *location = _editor->find_location_from_marker (_marker, is_start);
2722 _editor->_dragging_edit_point = true;
2724 update_item (location);
2726 // _drag_line->show();
2727 // _line->raise_to_top();
2730 show_verbose_cursor_time (location->start());
2732 show_verbose_cursor_time (location->end());
2735 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2738 case Selection::Toggle:
2739 /* we toggle on the button release */
2741 case Selection::Set:
2742 if (!_editor->selection->selected (_marker)) {
2743 _editor->selection->set (_marker);
2746 case Selection::Extend:
2748 Locations::LocationList ll;
2749 list<Marker*> to_add;
2751 _editor->selection->markers.range (s, e);
2752 s = min (_marker->position(), s);
2753 e = max (_marker->position(), e);
2756 if (e < max_framepos) {
2759 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2760 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2761 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2764 to_add.push_back (lm->start);
2767 to_add.push_back (lm->end);
2771 if (!to_add.empty()) {
2772 _editor->selection->add (to_add);
2776 case Selection::Add:
2777 _editor->selection->add (_marker);
2781 /* Set up copies for us to manipulate during the drag
2784 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2786 Location* l = _editor->find_location_from_marker (*i, is_start);
2793 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2795 /* range: check that the other end of the range isn't
2798 CopiedLocationInfo::iterator x;
2799 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2800 if (*(*x).location == *l) {
2804 if (x == _copied_locations.end()) {
2805 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2807 (*x).markers.push_back (*i);
2808 (*x).move_both = true;
2816 MarkerDrag::setup_pointer_frame_offset ()
2819 Location *location = _editor->find_location_from_marker (_marker, is_start);
2820 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2824 MarkerDrag::motion (GdkEvent* event, bool)
2826 framecnt_t f_delta = 0;
2828 bool move_both = false;
2829 Location *real_location;
2830 Location *copy_location = 0;
2832 framepos_t const newframe = adjusted_current_frame (event);
2833 framepos_t next = newframe;
2835 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2839 CopiedLocationInfo::iterator x;
2841 /* find the marker we're dragging, and compute the delta */
2843 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2845 copy_location = (*x).location;
2847 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2849 /* this marker is represented by this
2850 * CopiedLocationMarkerInfo
2853 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2858 if (real_location->is_mark()) {
2859 f_delta = newframe - copy_location->start();
2863 switch (_marker->type()) {
2864 case Marker::SessionStart:
2865 case Marker::RangeStart:
2866 case Marker::LoopStart:
2867 case Marker::PunchIn:
2868 f_delta = newframe - copy_location->start();
2871 case Marker::SessionEnd:
2872 case Marker::RangeEnd:
2873 case Marker::LoopEnd:
2874 case Marker::PunchOut:
2875 f_delta = newframe - copy_location->end();
2878 /* what kind of marker is this ? */
2887 if (x == _copied_locations.end()) {
2888 /* hmm, impossible - we didn't find the dragged marker */
2892 /* now move them all */
2894 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2896 copy_location = x->location;
2898 /* call this to find out if its the start or end */
2900 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2904 if (real_location->locked()) {
2908 if (copy_location->is_mark()) {
2912 copy_location->set_start (copy_location->start() + f_delta);
2916 framepos_t new_start = copy_location->start() + f_delta;
2917 framepos_t new_end = copy_location->end() + f_delta;
2919 if (is_start) { // start-of-range marker
2921 if (move_both || (*x).move_both) {
2922 copy_location->set_start (new_start);
2923 copy_location->set_end (new_end);
2924 } else if (new_start < copy_location->end()) {
2925 copy_location->set_start (new_start);
2926 } else if (newframe > 0) {
2927 _editor->snap_to (next, 1, true);
2928 copy_location->set_end (next);
2929 copy_location->set_start (newframe);
2932 } else { // end marker
2934 if (move_both || (*x).move_both) {
2935 copy_location->set_end (new_end);
2936 copy_location->set_start (new_start);
2937 } else if (new_end > copy_location->start()) {
2938 copy_location->set_end (new_end);
2939 } else if (newframe > 0) {
2940 _editor->snap_to (next, -1, true);
2941 copy_location->set_start (next);
2942 copy_location->set_end (newframe);
2947 update_item (copy_location);
2949 /* now lookup the actual GUI items used to display this
2950 * location and move them to wherever the copy of the location
2951 * is now. This means that the logic in ARDOUR::Location is
2952 * still enforced, even though we are not (yet) modifying
2953 * the real Location itself.
2956 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2959 lm->set_position (copy_location->start(), copy_location->end());
2964 assert (!_copied_locations.empty());
2966 show_verbose_cursor_time (newframe);
2970 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2972 if (!movement_occurred) {
2974 /* just a click, do nothing but finish
2975 off the selection process
2978 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2981 case Selection::Set:
2982 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2983 _editor->selection->set (_marker);
2987 case Selection::Toggle:
2988 /* we toggle on the button release, click only */
2989 _editor->selection->toggle (_marker);
2992 case Selection::Extend:
2993 case Selection::Add:
3000 _editor->_dragging_edit_point = false;
3002 _editor->begin_reversible_command ( _("move marker") );
3003 XMLNode &before = _editor->session()->locations()->get_state();
3005 MarkerSelection::iterator i;
3006 CopiedLocationInfo::iterator x;
3009 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3010 x != _copied_locations.end() && i != _editor->selection->markers.end();
3013 Location * location = _editor->find_location_from_marker (*i, is_start);
3017 if (location->locked()) {
3021 if (location->is_mark()) {
3022 location->set_start (((*x).location)->start());
3024 location->set (((*x).location)->start(), ((*x).location)->end());
3029 XMLNode &after = _editor->session()->locations()->get_state();
3030 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3031 _editor->commit_reversible_command ();
3035 MarkerDrag::aborted (bool)
3041 MarkerDrag::update_item (Location*)
3046 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3048 _cumulative_x_drag (0),
3049 _cumulative_y_drag (0)
3051 if (_zero_gain_fraction < 0.0) {
3052 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3055 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3057 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3063 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3065 Drag::start_grab (event, _editor->cursors()->fader);
3067 // start the grab at the center of the control point so
3068 // the point doesn't 'jump' to the mouse after the first drag
3069 _fixed_grab_x = _point->get_x();
3070 _fixed_grab_y = _point->get_y();
3072 float const fraction = 1 - (_point->get_y() / _point->line().height());
3074 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3076 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3077 event->button.x + 10, event->button.y + 10);
3079 _editor->verbose_cursor()->show ();
3081 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3083 if (!_point->can_slide ()) {
3084 _x_constrained = true;
3089 ControlPointDrag::motion (GdkEvent* event, bool)
3091 double dx = _drags->current_pointer_x() - last_pointer_x();
3092 double dy = _drags->current_pointer_y() - last_pointer_y();
3094 if (event->button.state & Keyboard::SecondaryModifier) {
3099 /* coordinate in pixels relative to the start of the region (for region-based automation)
3100 or track (for track-based automation) */
3101 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3102 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3104 // calculate zero crossing point. back off by .01 to stay on the
3105 // positive side of zero
3106 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3108 // make sure we hit zero when passing through
3109 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3113 if (_x_constrained) {
3116 if (_y_constrained) {
3120 _cumulative_x_drag = cx - _fixed_grab_x;
3121 _cumulative_y_drag = cy - _fixed_grab_y;
3125 cy = min ((double) _point->line().height(), cy);
3127 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3129 if (!_x_constrained) {
3130 _editor->snap_to_with_modifier (cx_frames, event);
3133 cx_frames = min (cx_frames, _point->line().maximum_time());
3135 float const fraction = 1.0 - (cy / _point->line().height());
3137 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3139 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3143 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3145 if (!movement_occurred) {
3149 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3150 _editor->reset_point_selection ();
3154 motion (event, false);
3157 _point->line().end_drag (_pushing, _final_index);
3158 _editor->session()->commit_reversible_command ();
3162 ControlPointDrag::aborted (bool)
3164 _point->line().reset ();
3168 ControlPointDrag::active (Editing::MouseMode m)
3170 if (m == Editing::MouseGain) {
3171 /* always active in mouse gain */
3175 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3176 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3179 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3182 _cumulative_y_drag (0)
3184 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3188 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3190 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3193 _item = &_line->grab_item ();
3195 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3196 origin, and ditto for y.
3199 double cx = event->button.x;
3200 double cy = event->button.y;
3202 _line->parent_group().canvas_to_item (cx, cy);
3204 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3209 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3210 /* no adjacent points */
3214 Drag::start_grab (event, _editor->cursors()->fader);
3216 /* store grab start in parent frame */
3221 double fraction = 1.0 - (cy / _line->height());
3223 _line->start_drag_line (before, after, fraction);
3225 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3226 event->button.x + 10, event->button.y + 10);
3228 _editor->verbose_cursor()->show ();
3232 LineDrag::motion (GdkEvent* event, bool)
3234 double dy = _drags->current_pointer_y() - last_pointer_y();
3236 if (event->button.state & Keyboard::SecondaryModifier) {
3240 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3242 _cumulative_y_drag = cy - _fixed_grab_y;
3245 cy = min ((double) _line->height(), cy);
3247 double const fraction = 1.0 - (cy / _line->height());
3250 /* we are ignoring x position for this drag, so we can just pass in anything */
3251 _line->drag_motion (0, fraction, true, false, ignored);
3253 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3257 LineDrag::finished (GdkEvent* event, bool)
3259 motion (event, false);
3260 _line->end_drag (false, 0);
3261 _editor->session()->commit_reversible_command ();
3265 LineDrag::aborted (bool)
3270 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3273 _cumulative_x_drag (0)
3275 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3279 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3281 Drag::start_grab (event);
3283 _line = reinterpret_cast<Line*> (_item);
3286 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3288 double cx = event->button.x;
3289 double cy = event->button.y;
3291 _item->parent()->canvas_to_item (cx, cy);
3293 /* store grab start in parent frame */
3294 _region_view_grab_x = cx;
3296 _before = *(float*) _item->get_data ("position");
3298 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3300 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3304 FeatureLineDrag::motion (GdkEvent*, bool)
3306 double dx = _drags->current_pointer_x() - last_pointer_x();
3308 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3310 _cumulative_x_drag += dx;
3312 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3321 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3323 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3325 float *pos = new float;
3328 _line->set_data ("position", pos);
3334 FeatureLineDrag::finished (GdkEvent*, bool)
3336 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3337 _arv->update_transient(_before, _before);
3341 FeatureLineDrag::aborted (bool)
3346 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3348 , _vertical_only (false)
3350 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3354 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3356 Drag::start_grab (event);
3357 show_verbose_cursor_time (adjusted_current_frame (event));
3361 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3368 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3370 framepos_t grab = grab_frame ();
3371 if (Config->get_rubberbanding_snaps_to_grid ()) {
3372 _editor->snap_to_with_modifier (grab, event);
3375 /* base start and end on initial click position */
3385 if (_drags->current_pointer_y() < grab_y()) {
3386 y1 = _drags->current_pointer_y();
3389 y2 = _drags->current_pointer_y();
3394 if (start != end || y1 != y2) {
3396 double x1 = _editor->sample_to_pixel (start);
3397 double x2 = _editor->sample_to_pixel (end);
3399 _editor->rubberband_rect->set_x0 (x1);
3400 if (_vertical_only) {
3401 /* fixed 10 pixel width */
3402 _editor->rubberband_rect->set_x1 (x1 + 10);
3404 _editor->rubberband_rect->set_x1 (x2);
3407 _editor->rubberband_rect->set_y0 (y1);
3408 _editor->rubberband_rect->set_y1 (y2);
3410 _editor->rubberband_rect->show();
3411 _editor->rubberband_rect->raise_to_top();
3413 show_verbose_cursor_time (pf);
3415 do_select_things (event, true);
3420 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3425 if (grab_frame() < last_pointer_frame()) {
3427 x2 = last_pointer_frame ();
3430 x1 = last_pointer_frame ();
3436 if (_drags->current_pointer_y() < grab_y()) {
3437 y1 = _drags->current_pointer_y();
3440 y2 = _drags->current_pointer_y();
3444 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3448 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3450 if (movement_occurred) {
3452 motion (event, false);
3453 do_select_things (event, false);
3459 bool do_deselect = true;
3460 MidiTimeAxisView* mtv;
3462 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3464 if (_editor->selection->empty()) {
3465 /* nothing selected */
3466 add_midi_region (mtv);
3467 do_deselect = false;
3471 /* do not deselect if Primary or Tertiary (toggle-select or
3472 * extend-select are pressed.
3475 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3476 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3483 _editor->rubberband_rect->hide();
3487 RubberbandSelectDrag::aborted (bool)
3489 _editor->rubberband_rect->hide ();
3492 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3493 : RegionDrag (e, i, p, v)
3495 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3499 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3501 Drag::start_grab (event, cursor);
3503 show_verbose_cursor_time (adjusted_current_frame (event));
3507 TimeFXDrag::motion (GdkEvent* event, bool)
3509 RegionView* rv = _primary;
3510 StreamView* cv = rv->get_time_axis_view().view ();
3512 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3513 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3514 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3516 framepos_t const pf = adjusted_current_frame (event);
3518 if (pf > rv->region()->position()) {
3519 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3522 show_verbose_cursor_time (pf);
3526 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3528 _primary->get_time_axis_view().hide_timestretch ();
3530 if (!movement_occurred) {
3534 if (last_pointer_frame() < _primary->region()->position()) {
3535 /* backwards drag of the left edge - not usable */
3539 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3541 float percentage = (double) newlen / (double) _primary->region()->length();
3543 #ifndef USE_RUBBERBAND
3544 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3545 if (_primary->region()->data_type() == DataType::AUDIO) {
3546 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3550 if (!_editor->get_selection().regions.empty()) {
3551 /* primary will already be included in the selection, and edit
3552 group shared editing will propagate selection across
3553 equivalent regions, so just use the current region
3557 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3558 error << _("An error occurred while executing time stretch operation") << endmsg;
3564 TimeFXDrag::aborted (bool)
3566 _primary->get_time_axis_view().hide_timestretch ();
3569 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3572 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3576 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3578 Drag::start_grab (event);
3582 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3584 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3588 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3590 if (movement_occurred && _editor->session()) {
3591 /* make sure we stop */
3592 _editor->session()->request_transport_speed (0.0);
3597 ScrubDrag::aborted (bool)
3602 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3607 , _original_pointer_time_axis (-1)
3608 , _last_pointer_time_axis (-1)
3609 , _time_selection_at_start (!_editor->get_selection().time.empty())
3611 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3613 if (_time_selection_at_start) {
3614 start_at_start = _editor->get_selection().time.start();
3615 end_at_start = _editor->get_selection().time.end_frame();
3620 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3622 if (_editor->session() == 0) {
3626 Gdk::Cursor* cursor = 0;
3628 switch (_operation) {
3629 case CreateSelection:
3630 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3635 cursor = _editor->cursors()->selector;
3636 Drag::start_grab (event, cursor);
3639 case SelectionStartTrim:
3640 if (_editor->clicked_axisview) {
3641 _editor->clicked_axisview->order_selection_trims (_item, true);
3643 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3646 case SelectionEndTrim:
3647 if (_editor->clicked_axisview) {
3648 _editor->clicked_axisview->order_selection_trims (_item, false);
3650 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3654 Drag::start_grab (event, cursor);
3657 case SelectionExtend:
3658 Drag::start_grab (event, cursor);
3662 if (_operation == SelectionMove) {
3663 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3665 show_verbose_cursor_time (adjusted_current_frame (event));
3668 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3672 SelectionDrag::setup_pointer_frame_offset ()
3674 switch (_operation) {
3675 case CreateSelection:
3676 _pointer_frame_offset = 0;
3679 case SelectionStartTrim:
3681 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3684 case SelectionEndTrim:
3685 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3688 case SelectionExtend:
3694 SelectionDrag::motion (GdkEvent* event, bool first_move)
3696 framepos_t start = 0;
3698 framecnt_t length = 0;
3699 framecnt_t distance = 0;
3701 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3702 if (pending_time_axis.first == 0) {
3706 framepos_t const pending_position = adjusted_current_frame (event);
3708 /* only alter selection if things have changed */
3710 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3714 switch (_operation) {
3715 case CreateSelection:
3717 framepos_t grab = grab_frame ();
3720 grab = adjusted_current_frame (event, false);
3721 if (grab < pending_position) {
3722 _editor->snap_to (grab, -1);
3724 _editor->snap_to (grab, 1);
3728 if (pending_position < grab) {
3729 start = pending_position;
3732 end = pending_position;
3736 /* first drag: Either add to the selection
3737 or create a new selection
3743 /* adding to the selection */
3744 _editor->set_selected_track_as_side_effect (Selection::Add);
3745 //_editor->selection->add (_editor->clicked_axisview);
3746 _editor->clicked_selection = _editor->selection->add (start, end);
3751 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3752 //_editor->selection->set (_editor->clicked_axisview);
3753 _editor->set_selected_track_as_side_effect (Selection::Set);
3756 _editor->clicked_selection = _editor->selection->set (start, end);
3760 /* select the track that we're in */
3761 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3762 // _editor->set_selected_track_as_side_effect (Selection::Add);
3763 _editor->selection->add (pending_time_axis.first);
3764 _added_time_axes.push_back (pending_time_axis.first);
3767 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3768 tracks that we selected in the first place.
3771 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3772 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3774 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3775 while (i != _added_time_axes.end()) {
3777 list<TimeAxisView*>::iterator tmp = i;
3780 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3781 _editor->selection->remove (*i);
3782 _added_time_axes.remove (*i);
3791 case SelectionStartTrim:
3793 start = _editor->selection->time[_editor->clicked_selection].start;
3794 end = _editor->selection->time[_editor->clicked_selection].end;
3796 if (pending_position > end) {
3799 start = pending_position;
3803 case SelectionEndTrim:
3805 start = _editor->selection->time[_editor->clicked_selection].start;
3806 end = _editor->selection->time[_editor->clicked_selection].end;
3808 if (pending_position < start) {
3811 end = pending_position;
3818 start = _editor->selection->time[_editor->clicked_selection].start;
3819 end = _editor->selection->time[_editor->clicked_selection].end;
3821 length = end - start;
3822 distance = pending_position - start;
3823 start = pending_position;
3824 _editor->snap_to (start);
3826 end = start + length;
3830 case SelectionExtend:
3834 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3835 _editor->start_canvas_autoscroll (1, 0);
3839 switch (_operation) {
3841 if (_time_selection_at_start) {
3842 _editor->selection->move_time (distance);
3846 _editor->selection->replace (_editor->clicked_selection, start, end);
3850 if (_operation == SelectionMove) {
3851 show_verbose_cursor_time(start);
3853 show_verbose_cursor_time(pending_position);
3858 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3860 Session* s = _editor->session();
3862 if (movement_occurred) {
3863 motion (event, false);
3864 /* XXX this is not object-oriented programming at all. ick */
3865 if (_editor->selection->time.consolidate()) {
3866 _editor->selection->TimeChanged ();
3869 /* XXX what if its a music time selection? */
3871 if ( s->get_play_range() && s->transport_rolling() ) {
3872 s->request_play_range (&_editor->selection->time, true);
3874 if (Config->get_always_play_range() && !s->transport_rolling()) {
3875 s->request_locate (_editor->get_selection().time.start());
3881 /* just a click, no pointer movement.
3884 if (_operation == SelectionExtend) {
3885 if (_time_selection_at_start) {
3886 framepos_t pos = adjusted_current_frame (event, false);
3887 framepos_t start = min (pos, start_at_start);
3888 framepos_t end = max (pos, end_at_start);
3889 _editor->selection->set (start, end);
3892 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3893 if (_editor->clicked_selection) {
3894 _editor->selection->remove (_editor->clicked_selection);
3897 if (!_editor->clicked_selection) {
3898 _editor->selection->clear_time();
3903 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3904 _editor->selection->set (_editor->clicked_axisview);
3907 if (s && s->get_play_range () && s->transport_rolling()) {
3908 s->request_stop (false, false);
3913 _editor->stop_canvas_autoscroll ();
3914 _editor->clicked_selection = 0;
3918 SelectionDrag::aborted (bool)
3923 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3928 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3930 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
3931 ArdourCanvas::Rect (0.0, 0.0, 0.0,
3932 physical_screen_height (_editor->get_window())));
3933 _drag_rect->hide ();
3935 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3936 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3940 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3942 if (_editor->session() == 0) {
3946 Gdk::Cursor* cursor = 0;
3948 if (!_editor->temp_location) {
3949 _editor->temp_location = new Location (*_editor->session());
3952 switch (_operation) {
3953 case CreateRangeMarker:
3954 case CreateTransportMarker:
3955 case CreateCDMarker:
3957 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3962 cursor = _editor->cursors()->selector;
3966 Drag::start_grab (event, cursor);
3968 show_verbose_cursor_time (adjusted_current_frame (event));
3972 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3974 framepos_t start = 0;
3976 ArdourCanvas::Rectangle *crect;
3978 switch (_operation) {
3979 case CreateRangeMarker:
3980 crect = _editor->range_bar_drag_rect;
3982 case CreateTransportMarker:
3983 crect = _editor->transport_bar_drag_rect;
3985 case CreateCDMarker:
3986 crect = _editor->cd_marker_bar_drag_rect;
3989 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
3994 framepos_t const pf = adjusted_current_frame (event);
3996 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3997 framepos_t grab = grab_frame ();
3998 _editor->snap_to (grab);
4000 if (pf < grab_frame()) {
4008 /* first drag: Either add to the selection
4009 or create a new selection.
4014 _editor->temp_location->set (start, end);
4018 update_item (_editor->temp_location);
4020 //_drag_rect->raise_to_top();
4025 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4026 _editor->start_canvas_autoscroll (1, 0);
4030 _editor->temp_location->set (start, end);
4032 double x1 = _editor->sample_to_pixel (start);
4033 double x2 = _editor->sample_to_pixel (end);
4037 update_item (_editor->temp_location);
4040 show_verbose_cursor_time (pf);
4045 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4047 Location * newloc = 0;
4051 if (movement_occurred) {
4052 motion (event, false);
4055 switch (_operation) {
4056 case CreateRangeMarker:
4057 case CreateCDMarker:
4059 _editor->begin_reversible_command (_("new range marker"));
4060 XMLNode &before = _editor->session()->locations()->get_state();
4061 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4062 if (_operation == CreateCDMarker) {
4063 flags = Location::IsRangeMarker | Location::IsCDMarker;
4064 _editor->cd_marker_bar_drag_rect->hide();
4067 flags = Location::IsRangeMarker;
4068 _editor->range_bar_drag_rect->hide();
4070 newloc = new Location (
4071 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4074 _editor->session()->locations()->add (newloc, true);
4075 XMLNode &after = _editor->session()->locations()->get_state();
4076 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4077 _editor->commit_reversible_command ();
4081 case CreateTransportMarker:
4082 // popup menu to pick loop or punch
4083 _editor->new_transport_marker_context_menu (&event->button, _item);
4087 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4089 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4094 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4096 if (end == max_framepos) {
4097 end = _editor->session()->current_end_frame ();
4100 if (start == max_framepos) {
4101 start = _editor->session()->current_start_frame ();
4104 switch (_editor->mouse_mode) {
4106 /* find the two markers on either side and then make the selection from it */
4107 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4111 /* find the two markers on either side of the click and make the range out of it */
4112 _editor->selection->set (start, end);
4121 _editor->stop_canvas_autoscroll ();
4125 RangeMarkerBarDrag::aborted (bool)
4131 RangeMarkerBarDrag::update_item (Location* location)
4133 double const x1 = _editor->sample_to_pixel (location->start());
4134 double const x2 = _editor->sample_to_pixel (location->end());
4136 _drag_rect->set_x0 (x1);
4137 _drag_rect->set_x1 (x2);
4140 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4144 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4148 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4150 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4151 Drag::start_grab (event, _editor->cursors()->zoom_out);
4154 Drag::start_grab (event, _editor->cursors()->zoom_in);
4158 show_verbose_cursor_time (adjusted_current_frame (event));
4162 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4167 framepos_t const pf = adjusted_current_frame (event);
4169 framepos_t grab = grab_frame ();
4170 _editor->snap_to_with_modifier (grab, event);
4172 /* base start and end on initial click position */
4184 _editor->zoom_rect->show();
4185 _editor->zoom_rect->raise_to_top();
4188 _editor->reposition_zoom_rect(start, end);
4190 show_verbose_cursor_time (pf);
4195 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4197 if (movement_occurred) {
4198 motion (event, false);
4200 if (grab_frame() < last_pointer_frame()) {
4201 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4203 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4206 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4207 _editor->tav_zoom_step (_zoom_out);
4209 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4213 _editor->zoom_rect->hide();
4217 MouseZoomDrag::aborted (bool)
4219 _editor->zoom_rect->hide ();
4222 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4224 , _cumulative_dx (0)
4225 , _cumulative_dy (0)
4227 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4229 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4231 _region = &_primary->region_view ();
4232 _note_height = _region->midi_stream_view()->note_height ();
4236 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4238 Drag::start_grab (event);
4240 if (!(_was_selected = _primary->selected())) {
4242 /* tertiary-click means extend selection - we'll do that on button release,
4243 so don't add it here, because otherwise we make it hard to figure
4244 out the "extend-to" range.
4247 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4250 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4253 _region->note_selected (_primary, true);
4255 _region->unique_select (_primary);
4261 /** @return Current total drag x change in frames */
4263 NoteDrag::total_dx () const
4266 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4268 /* primary note time */
4269 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4271 /* new time of the primary note in session frames */
4272 frameoffset_t st = n + dx;
4274 framepos_t const rp = _region->region()->position ();
4276 /* prevent the note being dragged earlier than the region's position */
4279 /* snap and return corresponding delta */
4280 return _region->snap_frame_to_frame (st - rp) + rp - n;
4283 /** @return Current total drag y change in note number */
4285 NoteDrag::total_dy () const
4287 MidiStreamView* msv = _region->midi_stream_view ();
4288 double const y = _region->midi_view()->y_position ();
4289 /* new current note */
4290 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4292 n = max (msv->lowest_note(), n);
4293 n = min (msv->highest_note(), n);
4294 /* and work out delta */
4295 return n - msv->y_to_note (grab_y() - y);
4299 NoteDrag::motion (GdkEvent *, bool)
4301 /* Total change in x and y since the start of the drag */
4302 frameoffset_t const dx = total_dx ();
4303 int8_t const dy = total_dy ();
4305 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4306 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4307 double const tdy = -dy * _note_height - _cumulative_dy;
4310 _cumulative_dx += tdx;
4311 _cumulative_dy += tdy;
4313 int8_t note_delta = total_dy();
4315 _region->move_selection (tdx, tdy, note_delta);
4317 /* the new note value may be the same as the old one, but we
4318 * don't know what that means because the selection may have
4319 * involved more than one note and we might be doing something
4320 * odd with them. so show the note value anyway, always.
4324 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4326 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4327 (int) floor (new_note));
4329 show_verbose_cursor_text (buf);
4334 NoteDrag::finished (GdkEvent* ev, bool moved)
4337 /* no motion - select note */
4339 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4340 _editor->current_mouse_mode() == Editing::MouseDraw) {
4342 if (_was_selected) {
4343 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4345 _region->note_deselected (_primary);
4348 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4349 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4351 if (!extend && !add && _region->selection_size() > 1) {
4352 _region->unique_select (_primary);
4353 } else if (extend) {
4354 _region->note_selected (_primary, true, true);
4356 /* it was added during button press */
4361 _region->note_dropped (_primary, total_dx(), total_dy());
4366 NoteDrag::aborted (bool)
4371 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4372 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4373 : Drag (editor, atv->base_item ())
4375 , _nothing_to_drag (false)
4377 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4378 y_origin = atv->y_position();
4379 setup (atv->lines ());
4382 /** Make an AutomationRangeDrag for region gain lines */
4383 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4384 : Drag (editor, rv->get_canvas_group ())
4386 , _nothing_to_drag (false)
4388 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4390 list<boost::shared_ptr<AutomationLine> > lines;
4391 lines.push_back (rv->get_gain_line ());
4392 y_origin = rv->get_time_axis_view().y_position();
4396 /** @param lines AutomationLines to drag.
4397 * @param offset Offset from the session start to the points in the AutomationLines.
4400 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4402 /* find the lines that overlap the ranges being dragged */
4403 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4404 while (i != lines.end ()) {
4405 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4408 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4410 /* check this range against all the AudioRanges that we are using */
4411 list<AudioRange>::const_iterator k = _ranges.begin ();
4412 while (k != _ranges.end()) {
4413 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4419 /* add it to our list if it overlaps at all */
4420 if (k != _ranges.end()) {
4425 _lines.push_back (n);
4431 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4435 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4437 return 1.0 - ((global_y - y_origin) / line->height());
4441 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4443 Drag::start_grab (event, cursor);
4445 /* Get line states before we start changing things */
4446 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4447 i->state = &i->line->get_state ();
4448 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4451 if (_ranges.empty()) {
4453 /* No selected time ranges: drag all points */
4454 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4455 uint32_t const N = i->line->npoints ();
4456 for (uint32_t j = 0; j < N; ++j) {
4457 i->points.push_back (i->line->nth (j));
4463 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4465 framecnt_t const half = (i->start + i->end) / 2;
4467 /* find the line that this audio range starts in */
4468 list<Line>::iterator j = _lines.begin();
4469 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4473 if (j != _lines.end()) {
4474 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4476 /* j is the line that this audio range starts in; fade into it;
4477 64 samples length plucked out of thin air.
4480 framepos_t a = i->start + 64;
4485 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4486 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4488 the_list->add (p, the_list->eval (p));
4489 the_list->add (q, the_list->eval (q));
4492 /* same thing for the end */
4495 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4499 if (j != _lines.end()) {
4500 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4502 /* j is the line that this audio range starts in; fade out of it;
4503 64 samples length plucked out of thin air.
4506 framepos_t b = i->end - 64;
4511 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4512 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4514 the_list->add (p, the_list->eval (p));
4515 the_list->add (q, the_list->eval (q));
4519 _nothing_to_drag = true;
4521 /* Find all the points that should be dragged and put them in the relevant
4522 points lists in the Line structs.
4525 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4527 uint32_t const N = i->line->npoints ();
4528 for (uint32_t j = 0; j < N; ++j) {
4530 /* here's a control point on this line */
4531 ControlPoint* p = i->line->nth (j);
4532 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4534 /* see if it's inside a range */
4535 list<AudioRange>::const_iterator k = _ranges.begin ();
4536 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4540 if (k != _ranges.end()) {
4541 /* dragging this point */
4542 _nothing_to_drag = false;
4543 i->points.push_back (p);
4549 if (_nothing_to_drag) {
4553 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4554 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4559 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4561 if (_nothing_to_drag) {
4565 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4566 float const f = y_fraction (l->line, _drags->current_pointer_y());
4567 /* we are ignoring x position for this drag, so we can just pass in anything */
4569 l->line->drag_motion (0, f, true, false, ignored);
4570 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4575 AutomationRangeDrag::finished (GdkEvent* event, bool)
4577 if (_nothing_to_drag) {
4581 motion (event, false);
4582 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4583 i->line->end_drag (false, 0);
4586 _editor->session()->commit_reversible_command ();
4590 AutomationRangeDrag::aborted (bool)
4592 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4597 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4600 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4601 layer = v->region()->layer ();
4602 initial_y = v->get_canvas_group()->position().y;
4603 initial_playlist = v->region()->playlist ();
4604 initial_position = v->region()->position ();
4605 initial_end = v->region()->position () + v->region()->length ();
4608 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4609 : Drag (e, i->canvas_item ())
4612 , _cumulative_dx (0)
4614 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4615 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4620 PatchChangeDrag::motion (GdkEvent* ev, bool)
4622 framepos_t f = adjusted_current_frame (ev);
4623 boost::shared_ptr<Region> r = _region_view->region ();
4624 f = max (f, r->position ());
4625 f = min (f, r->last_frame ());
4627 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4628 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4629 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4630 _cumulative_dx = dxu;
4634 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4636 if (!movement_occurred) {
4640 boost::shared_ptr<Region> r (_region_view->region ());
4641 framepos_t f = adjusted_current_frame (ev);
4642 f = max (f, r->position ());
4643 f = min (f, r->last_frame ());
4645 _region_view->move_patch_change (
4647 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4652 PatchChangeDrag::aborted (bool)
4654 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4658 PatchChangeDrag::setup_pointer_frame_offset ()
4660 boost::shared_ptr<Region> region = _region_view->region ();
4661 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4664 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4665 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4672 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4674 framepos_t const p = _region_view->region()->position ();
4675 double const y = _region_view->midi_view()->y_position ();
4677 x1 = max ((framepos_t) 0, x1 - p);
4678 x2 = max ((framepos_t) 0, x2 - p);
4679 y1 = max (0.0, y1 - y);
4680 y2 = max (0.0, y2 - y);
4682 _region_view->update_drag_selection (
4683 _editor->sample_to_pixel (x1),
4684 _editor->sample_to_pixel (x2),
4687 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4692 MidiRubberbandSelectDrag::deselect_things ()
4697 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4698 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4701 _vertical_only = true;
4705 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4707 double const y = _region_view->midi_view()->y_position ();
4709 y1 = max (0.0, y1 - y);
4710 y2 = max (0.0, y2 - y);
4712 _region_view->update_vertical_drag_selection (
4715 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4720 MidiVerticalSelectDrag::deselect_things ()
4725 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4726 : RubberbandSelectDrag (e, i)
4732 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4734 if (drag_in_progress) {
4735 /* We just want to select things at the end of the drag, not during it */
4739 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4741 _editor->begin_reversible_command (_("rubberband selection"));
4742 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4743 _editor->commit_reversible_command ();
4747 EditorRubberbandSelectDrag::deselect_things ()
4749 if (!getenv("ARDOUR_SAE")) {
4750 _editor->selection->clear_tracks();
4752 _editor->selection->clear_regions();
4753 _editor->selection->clear_points ();
4754 _editor->selection->clear_lines ();
4757 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4765 NoteCreateDrag::~NoteCreateDrag ()
4771 NoteCreateDrag::grid_frames (framepos_t t) const
4774 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4779 return _region_view->region_beats_to_region_frames (grid_beats);
4783 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4785 Drag::start_grab (event, cursor);
4787 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4789 framepos_t pf = _drags->current_pointer_frame ();
4790 framecnt_t const g = grid_frames (pf);
4792 /* Hack so that we always snap to the note that we are over, instead of snapping
4793 to the next one if we're more than halfway through the one we're over.
4795 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4799 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4801 MidiStreamView* sv = _region_view->midi_stream_view ();
4802 double const x = _editor->sample_to_pixel (_note[0]);
4803 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4805 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4806 _drag_rect->set_outline_what (0xff);
4807 _drag_rect->set_outline_color (0xffffff99);
4808 _drag_rect->set_fill_color (0xffffff66);
4812 NoteCreateDrag::motion (GdkEvent* event, bool)
4814 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4815 double const x = _editor->sample_to_pixel (_note[1]);
4816 if (_note[1] > _note[0]) {
4817 _drag_rect->set_x1 (x);
4819 _drag_rect->set_x0 (x);
4824 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4826 if (!had_movement) {
4830 framepos_t const start = min (_note[0], _note[1]);
4831 framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4833 framecnt_t const g = grid_frames (start);
4834 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4836 if (_editor->snap_mode() == SnapNormal && length < g) {
4837 length = g - one_tick;
4840 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4842 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4846 NoteCreateDrag::y_to_region (double y) const
4849 _region_view->get_canvas_group()->canvas_to_item (x, y);
4854 NoteCreateDrag::aborted (bool)
4859 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4864 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4868 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4870 Drag::start_grab (event, cursor);
4874 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4880 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4883 distance = _drags->current_pointer_x() - grab_x();
4884 len = ar->fade_in()->back()->when;
4886 distance = grab_x() - _drags->current_pointer_x();
4887 len = ar->fade_out()->back()->when;
4890 /* how long should it be ? */
4892 new_length = len + _editor->pixel_to_sample (distance);
4894 /* now check with the region that this is legal */
4896 new_length = ar->verify_xfade_bounds (new_length, start);
4899 arv->redraw_start_xfade_to (ar, new_length);
4901 arv->redraw_end_xfade_to (ar, new_length);
4906 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4912 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4915 distance = _drags->current_pointer_x() - grab_x();
4916 len = ar->fade_in()->back()->when;
4918 distance = grab_x() - _drags->current_pointer_x();
4919 len = ar->fade_out()->back()->when;
4922 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
4924 _editor->begin_reversible_command ("xfade trim");
4925 ar->playlist()->clear_owned_changes ();
4928 ar->set_fade_in_length (new_length);
4930 ar->set_fade_out_length (new_length);
4933 /* Adjusting the xfade may affect other regions in the playlist, so we need
4934 to get undo Commands from the whole playlist rather than just the
4938 vector<Command*> cmds;
4939 ar->playlist()->rdiff (cmds);
4940 _editor->session()->add_commands (cmds);
4941 _editor->commit_reversible_command ();
4946 CrossfadeEdgeDrag::aborted (bool)
4949 arv->redraw_start_xfade ();
4951 arv->redraw_end_xfade ();