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, ArdourCanvas::Item* i, bool s)
2342 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2345 /** Do all the things we do when dragging the playhead to make it look as though
2346 * we have located, without actually doing the locate (because that would cause
2347 * the diskstream buffers to be refilled, which is too slow).
2350 CursorDrag::fake_locate (framepos_t t)
2352 _editor->playhead_cursor->set_position (t);
2354 Session* s = _editor->session ();
2355 if (s->timecode_transmission_suspended ()) {
2356 framepos_t const f = _editor->playhead_cursor->current_frame ();
2357 s->send_mmc_locate (f);
2358 s->send_full_time_code (f);
2361 show_verbose_cursor_time (t);
2362 _editor->UpdateAllTransportClocks (t);
2366 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2368 Drag::start_grab (event, c);
2370 _grab_zoom = _editor->samples_per_pixel;
2372 framepos_t where = _editor->canvas_event_frame (event, 0, 0);
2373 _editor->snap_to_with_modifier (where, event);
2375 _editor->_dragging_playhead = true;
2377 Session* s = _editor->session ();
2380 if (_was_rolling && _stop) {
2384 if (s->is_auditioning()) {
2385 s->cancel_audition ();
2389 if (AudioEngine::instance()->connected()) {
2391 /* do this only if we're the engine is connected
2392 * because otherwise this request will never be
2393 * serviced and we'll busy wait forever. likewise,
2394 * notice if we are disconnected while waiting for the
2395 * request to be serviced.
2398 s->request_suspend_timecode_transmission ();
2399 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2400 /* twiddle our thumbs */
2405 fake_locate (where);
2409 CursorDrag::motion (GdkEvent* event, bool)
2411 framepos_t const adjusted_frame = adjusted_current_frame (event);
2412 if (adjusted_frame != last_pointer_frame()) {
2413 fake_locate (adjusted_frame);
2418 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2420 _editor->_dragging_playhead = false;
2422 if (!movement_occurred && _stop) {
2426 motion (event, false);
2428 Session* s = _editor->session ();
2430 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2431 _editor->_pending_locate_request = true;
2432 s->request_resume_timecode_transmission ();
2437 CursorDrag::aborted (bool)
2439 if (_editor->_dragging_playhead) {
2440 _editor->session()->request_resume_timecode_transmission ();
2441 _editor->_dragging_playhead = false;
2444 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2447 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2448 : RegionDrag (e, i, p, v)
2450 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2454 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2456 Drag::start_grab (event, cursor);
2458 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2459 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2461 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2465 FadeInDrag::setup_pointer_frame_offset ()
2467 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2468 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2469 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2473 FadeInDrag::motion (GdkEvent* event, bool)
2475 framecnt_t fade_length;
2477 framepos_t const pos = adjusted_current_frame (event);
2479 boost::shared_ptr<Region> region = _primary->region ();
2481 if (pos < (region->position() + 64)) {
2482 fade_length = 64; // this should be a minimum defined somewhere
2483 } else if (pos > region->last_frame()) {
2484 fade_length = region->length();
2486 fade_length = pos - region->position();
2489 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2491 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2497 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2500 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2504 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2506 if (!movement_occurred) {
2510 framecnt_t fade_length;
2512 framepos_t const pos = adjusted_current_frame (event);
2514 boost::shared_ptr<Region> region = _primary->region ();
2516 if (pos < (region->position() + 64)) {
2517 fade_length = 64; // this should be a minimum defined somewhere
2518 } else if (pos > region->last_frame()) {
2519 fade_length = region->length();
2521 fade_length = pos - region->position();
2524 _editor->begin_reversible_command (_("change fade in length"));
2526 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2528 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2534 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2535 XMLNode &before = alist->get_state();
2537 tmp->audio_region()->set_fade_in_length (fade_length);
2538 tmp->audio_region()->set_fade_in_active (true);
2540 XMLNode &after = alist->get_state();
2541 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2544 _editor->commit_reversible_command ();
2548 FadeInDrag::aborted (bool)
2550 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2551 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2557 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2561 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2562 : RegionDrag (e, i, p, v)
2564 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2568 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2570 Drag::start_grab (event, cursor);
2572 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2573 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2575 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2579 FadeOutDrag::setup_pointer_frame_offset ()
2581 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2582 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2583 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2587 FadeOutDrag::motion (GdkEvent* event, bool)
2589 framecnt_t fade_length;
2591 framepos_t const pos = adjusted_current_frame (event);
2593 boost::shared_ptr<Region> region = _primary->region ();
2595 if (pos > (region->last_frame() - 64)) {
2596 fade_length = 64; // this should really be a minimum fade defined somewhere
2598 else if (pos < region->position()) {
2599 fade_length = region->length();
2602 fade_length = region->last_frame() - pos;
2605 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2607 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2613 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2616 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2620 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2622 if (!movement_occurred) {
2626 framecnt_t fade_length;
2628 framepos_t const pos = adjusted_current_frame (event);
2630 boost::shared_ptr<Region> region = _primary->region ();
2632 if (pos > (region->last_frame() - 64)) {
2633 fade_length = 64; // this should really be a minimum fade defined somewhere
2635 else if (pos < region->position()) {
2636 fade_length = region->length();
2639 fade_length = region->last_frame() - pos;
2642 _editor->begin_reversible_command (_("change fade out length"));
2644 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2646 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2652 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2653 XMLNode &before = alist->get_state();
2655 tmp->audio_region()->set_fade_out_length (fade_length);
2656 tmp->audio_region()->set_fade_out_active (true);
2658 XMLNode &after = alist->get_state();
2659 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2662 _editor->commit_reversible_command ();
2666 FadeOutDrag::aborted (bool)
2668 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2669 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2675 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2679 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2682 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2684 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2687 _points.push_back (ArdourCanvas::Duple (0, 0));
2688 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2691 MarkerDrag::~MarkerDrag ()
2693 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2698 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2700 location = new Location (*l);
2701 markers.push_back (m);
2706 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2708 Drag::start_grab (event, cursor);
2712 Location *location = _editor->find_location_from_marker (_marker, is_start);
2713 _editor->_dragging_edit_point = true;
2715 update_item (location);
2717 // _drag_line->show();
2718 // _line->raise_to_top();
2721 show_verbose_cursor_time (location->start());
2723 show_verbose_cursor_time (location->end());
2726 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2729 case Selection::Toggle:
2730 /* we toggle on the button release */
2732 case Selection::Set:
2733 if (!_editor->selection->selected (_marker)) {
2734 _editor->selection->set (_marker);
2737 case Selection::Extend:
2739 Locations::LocationList ll;
2740 list<Marker*> to_add;
2742 _editor->selection->markers.range (s, e);
2743 s = min (_marker->position(), s);
2744 e = max (_marker->position(), e);
2747 if (e < max_framepos) {
2750 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2751 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2752 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2755 to_add.push_back (lm->start);
2758 to_add.push_back (lm->end);
2762 if (!to_add.empty()) {
2763 _editor->selection->add (to_add);
2767 case Selection::Add:
2768 _editor->selection->add (_marker);
2772 /* Set up copies for us to manipulate during the drag
2775 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2777 Location* l = _editor->find_location_from_marker (*i, is_start);
2784 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2786 /* range: check that the other end of the range isn't
2789 CopiedLocationInfo::iterator x;
2790 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2791 if (*(*x).location == *l) {
2795 if (x == _copied_locations.end()) {
2796 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2798 (*x).markers.push_back (*i);
2799 (*x).move_both = true;
2807 MarkerDrag::setup_pointer_frame_offset ()
2810 Location *location = _editor->find_location_from_marker (_marker, is_start);
2811 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2815 MarkerDrag::motion (GdkEvent* event, bool)
2817 framecnt_t f_delta = 0;
2819 bool move_both = false;
2820 Location *real_location;
2821 Location *copy_location = 0;
2823 framepos_t const newframe = adjusted_current_frame (event);
2824 framepos_t next = newframe;
2826 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2830 CopiedLocationInfo::iterator x;
2832 /* find the marker we're dragging, and compute the delta */
2834 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2836 copy_location = (*x).location;
2838 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2840 /* this marker is represented by this
2841 * CopiedLocationMarkerInfo
2844 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2849 if (real_location->is_mark()) {
2850 f_delta = newframe - copy_location->start();
2854 switch (_marker->type()) {
2855 case Marker::SessionStart:
2856 case Marker::RangeStart:
2857 case Marker::LoopStart:
2858 case Marker::PunchIn:
2859 f_delta = newframe - copy_location->start();
2862 case Marker::SessionEnd:
2863 case Marker::RangeEnd:
2864 case Marker::LoopEnd:
2865 case Marker::PunchOut:
2866 f_delta = newframe - copy_location->end();
2869 /* what kind of marker is this ? */
2878 if (x == _copied_locations.end()) {
2879 /* hmm, impossible - we didn't find the dragged marker */
2883 /* now move them all */
2885 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2887 copy_location = x->location;
2889 /* call this to find out if its the start or end */
2891 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2895 if (real_location->locked()) {
2899 if (copy_location->is_mark()) {
2903 copy_location->set_start (copy_location->start() + f_delta);
2907 framepos_t new_start = copy_location->start() + f_delta;
2908 framepos_t new_end = copy_location->end() + f_delta;
2910 if (is_start) { // start-of-range marker
2912 if (move_both || (*x).move_both) {
2913 copy_location->set_start (new_start);
2914 copy_location->set_end (new_end);
2915 } else if (new_start < copy_location->end()) {
2916 copy_location->set_start (new_start);
2917 } else if (newframe > 0) {
2918 _editor->snap_to (next, 1, true);
2919 copy_location->set_end (next);
2920 copy_location->set_start (newframe);
2923 } else { // end marker
2925 if (move_both || (*x).move_both) {
2926 copy_location->set_end (new_end);
2927 copy_location->set_start (new_start);
2928 } else if (new_end > copy_location->start()) {
2929 copy_location->set_end (new_end);
2930 } else if (newframe > 0) {
2931 _editor->snap_to (next, -1, true);
2932 copy_location->set_start (next);
2933 copy_location->set_end (newframe);
2938 update_item (copy_location);
2940 /* now lookup the actual GUI items used to display this
2941 * location and move them to wherever the copy of the location
2942 * is now. This means that the logic in ARDOUR::Location is
2943 * still enforced, even though we are not (yet) modifying
2944 * the real Location itself.
2947 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2950 lm->set_position (copy_location->start(), copy_location->end());
2955 assert (!_copied_locations.empty());
2957 show_verbose_cursor_time (newframe);
2961 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2963 if (!movement_occurred) {
2965 /* just a click, do nothing but finish
2966 off the selection process
2969 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2972 case Selection::Set:
2973 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2974 _editor->selection->set (_marker);
2978 case Selection::Toggle:
2979 /* we toggle on the button release, click only */
2980 _editor->selection->toggle (_marker);
2983 case Selection::Extend:
2984 case Selection::Add:
2991 _editor->_dragging_edit_point = false;
2993 _editor->begin_reversible_command ( _("move marker") );
2994 XMLNode &before = _editor->session()->locations()->get_state();
2996 MarkerSelection::iterator i;
2997 CopiedLocationInfo::iterator x;
3000 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3001 x != _copied_locations.end() && i != _editor->selection->markers.end();
3004 Location * location = _editor->find_location_from_marker (*i, is_start);
3008 if (location->locked()) {
3012 if (location->is_mark()) {
3013 location->set_start (((*x).location)->start());
3015 location->set (((*x).location)->start(), ((*x).location)->end());
3020 XMLNode &after = _editor->session()->locations()->get_state();
3021 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3022 _editor->commit_reversible_command ();
3026 MarkerDrag::aborted (bool)
3032 MarkerDrag::update_item (Location*)
3037 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3039 _cumulative_x_drag (0),
3040 _cumulative_y_drag (0)
3042 if (_zero_gain_fraction < 0.0) {
3043 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3046 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3048 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3054 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3056 Drag::start_grab (event, _editor->cursors()->fader);
3058 // start the grab at the center of the control point so
3059 // the point doesn't 'jump' to the mouse after the first drag
3060 _fixed_grab_x = _point->get_x();
3061 _fixed_grab_y = _point->get_y();
3063 float const fraction = 1 - (_point->get_y() / _point->line().height());
3065 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3067 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3068 event->button.x + 10, event->button.y + 10);
3070 _editor->verbose_cursor()->show ();
3072 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3074 if (!_point->can_slide ()) {
3075 _x_constrained = true;
3080 ControlPointDrag::motion (GdkEvent* event, bool)
3082 double dx = _drags->current_pointer_x() - last_pointer_x();
3083 double dy = _drags->current_pointer_y() - last_pointer_y();
3085 if (event->button.state & Keyboard::SecondaryModifier) {
3090 /* coordinate in pixels relative to the start of the region (for region-based automation)
3091 or track (for track-based automation) */
3092 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3093 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3095 // calculate zero crossing point. back off by .01 to stay on the
3096 // positive side of zero
3097 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3099 // make sure we hit zero when passing through
3100 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3104 if (_x_constrained) {
3107 if (_y_constrained) {
3111 _cumulative_x_drag = cx - _fixed_grab_x;
3112 _cumulative_y_drag = cy - _fixed_grab_y;
3116 cy = min ((double) _point->line().height(), cy);
3118 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3120 if (!_x_constrained) {
3121 _editor->snap_to_with_modifier (cx_frames, event);
3124 cx_frames = min (cx_frames, _point->line().maximum_time());
3126 float const fraction = 1.0 - (cy / _point->line().height());
3128 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3130 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3134 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3136 if (!movement_occurred) {
3140 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3141 _editor->reset_point_selection ();
3145 motion (event, false);
3148 _point->line().end_drag (_pushing, _final_index);
3149 _editor->session()->commit_reversible_command ();
3153 ControlPointDrag::aborted (bool)
3155 _point->line().reset ();
3159 ControlPointDrag::active (Editing::MouseMode m)
3161 if (m == Editing::MouseGain) {
3162 /* always active in mouse gain */
3166 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3167 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3170 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3173 _cumulative_y_drag (0)
3175 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3179 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3181 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3184 _item = &_line->grab_item ();
3186 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3187 origin, and ditto for y.
3190 double cx = event->button.x;
3191 double cy = event->button.y;
3193 _line->parent_group().canvas_to_item (cx, cy);
3195 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3200 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3201 /* no adjacent points */
3205 Drag::start_grab (event, _editor->cursors()->fader);
3207 /* store grab start in parent frame */
3212 double fraction = 1.0 - (cy / _line->height());
3214 _line->start_drag_line (before, after, fraction);
3216 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3217 event->button.x + 10, event->button.y + 10);
3219 _editor->verbose_cursor()->show ();
3223 LineDrag::motion (GdkEvent* event, bool)
3225 double dy = _drags->current_pointer_y() - last_pointer_y();
3227 if (event->button.state & Keyboard::SecondaryModifier) {
3231 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3233 _cumulative_y_drag = cy - _fixed_grab_y;
3236 cy = min ((double) _line->height(), cy);
3238 double const fraction = 1.0 - (cy / _line->height());
3241 /* we are ignoring x position for this drag, so we can just pass in anything */
3242 _line->drag_motion (0, fraction, true, false, ignored);
3244 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3248 LineDrag::finished (GdkEvent* event, bool)
3250 motion (event, false);
3251 _line->end_drag (false, 0);
3252 _editor->session()->commit_reversible_command ();
3256 LineDrag::aborted (bool)
3261 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3264 _cumulative_x_drag (0)
3266 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3270 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3272 Drag::start_grab (event);
3274 _line = reinterpret_cast<Line*> (_item);
3277 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3279 double cx = event->button.x;
3280 double cy = event->button.y;
3282 _item->parent()->canvas_to_item (cx, cy);
3284 /* store grab start in parent frame */
3285 _region_view_grab_x = cx;
3287 _before = *(float*) _item->get_data ("position");
3289 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3291 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3295 FeatureLineDrag::motion (GdkEvent*, bool)
3297 double dx = _drags->current_pointer_x() - last_pointer_x();
3299 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3301 _cumulative_x_drag += dx;
3303 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3312 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3314 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3316 float *pos = new float;
3319 _line->set_data ("position", pos);
3325 FeatureLineDrag::finished (GdkEvent*, bool)
3327 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3328 _arv->update_transient(_before, _before);
3332 FeatureLineDrag::aborted (bool)
3337 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3339 , _vertical_only (false)
3341 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3345 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3347 Drag::start_grab (event);
3348 show_verbose_cursor_time (adjusted_current_frame (event));
3352 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3359 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3361 framepos_t grab = grab_frame ();
3362 if (Config->get_rubberbanding_snaps_to_grid ()) {
3363 _editor->snap_to_with_modifier (grab, event);
3366 /* base start and end on initial click position */
3376 if (_drags->current_pointer_y() < grab_y()) {
3377 y1 = _drags->current_pointer_y();
3380 y2 = _drags->current_pointer_y();
3385 if (start != end || y1 != y2) {
3387 double x1 = _editor->sample_to_pixel (start);
3388 double x2 = _editor->sample_to_pixel (end);
3390 _editor->rubberband_rect->set_x0 (x1);
3391 if (_vertical_only) {
3392 /* fixed 10 pixel width */
3393 _editor->rubberband_rect->set_x1 (x1 + 10);
3395 _editor->rubberband_rect->set_x1 (x2);
3398 _editor->rubberband_rect->set_y0 (y1);
3399 _editor->rubberband_rect->set_y1 (y2);
3401 _editor->rubberband_rect->show();
3402 _editor->rubberband_rect->raise_to_top();
3404 show_verbose_cursor_time (pf);
3406 do_select_things (event, true);
3411 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3416 if (grab_frame() < last_pointer_frame()) {
3418 x2 = last_pointer_frame ();
3421 x1 = last_pointer_frame ();
3427 if (_drags->current_pointer_y() < grab_y()) {
3428 y1 = _drags->current_pointer_y();
3431 y2 = _drags->current_pointer_y();
3435 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3439 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3441 if (movement_occurred) {
3443 motion (event, false);
3444 do_select_things (event, false);
3450 bool do_deselect = true;
3451 MidiTimeAxisView* mtv;
3453 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3455 if (_editor->selection->empty()) {
3456 /* nothing selected */
3457 add_midi_region (mtv);
3458 do_deselect = false;
3462 /* do not deselect if Primary or Tertiary (toggle-select or
3463 * extend-select are pressed.
3466 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3467 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3474 _editor->rubberband_rect->hide();
3478 RubberbandSelectDrag::aborted (bool)
3480 _editor->rubberband_rect->hide ();
3483 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3484 : RegionDrag (e, i, p, v)
3486 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3490 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3492 Drag::start_grab (event, cursor);
3494 show_verbose_cursor_time (adjusted_current_frame (event));
3498 TimeFXDrag::motion (GdkEvent* event, bool)
3500 RegionView* rv = _primary;
3501 StreamView* cv = rv->get_time_axis_view().view ();
3503 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3504 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3505 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3507 framepos_t const pf = adjusted_current_frame (event);
3509 if (pf > rv->region()->position()) {
3510 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3513 show_verbose_cursor_time (pf);
3517 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3519 _primary->get_time_axis_view().hide_timestretch ();
3521 if (!movement_occurred) {
3525 if (last_pointer_frame() < _primary->region()->position()) {
3526 /* backwards drag of the left edge - not usable */
3530 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3532 float percentage = (double) newlen / (double) _primary->region()->length();
3534 #ifndef USE_RUBBERBAND
3535 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3536 if (_primary->region()->data_type() == DataType::AUDIO) {
3537 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3541 if (!_editor->get_selection().regions.empty()) {
3542 /* primary will already be included in the selection, and edit
3543 group shared editing will propagate selection across
3544 equivalent regions, so just use the current region
3548 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3549 error << _("An error occurred while executing time stretch operation") << endmsg;
3555 TimeFXDrag::aborted (bool)
3557 _primary->get_time_axis_view().hide_timestretch ();
3560 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3563 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3567 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3569 Drag::start_grab (event);
3573 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3575 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3579 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3581 if (movement_occurred && _editor->session()) {
3582 /* make sure we stop */
3583 _editor->session()->request_transport_speed (0.0);
3588 ScrubDrag::aborted (bool)
3593 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3598 , _original_pointer_time_axis (-1)
3599 , _last_pointer_time_axis (-1)
3600 , _time_selection_at_start (!_editor->get_selection().time.empty())
3602 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3604 if (_time_selection_at_start) {
3605 start_at_start = _editor->get_selection().time.start();
3606 end_at_start = _editor->get_selection().time.end_frame();
3611 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3613 if (_editor->session() == 0) {
3617 Gdk::Cursor* cursor = 0;
3619 switch (_operation) {
3620 case CreateSelection:
3621 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3626 cursor = _editor->cursors()->selector;
3627 Drag::start_grab (event, cursor);
3630 case SelectionStartTrim:
3631 if (_editor->clicked_axisview) {
3632 _editor->clicked_axisview->order_selection_trims (_item, true);
3634 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3637 case SelectionEndTrim:
3638 if (_editor->clicked_axisview) {
3639 _editor->clicked_axisview->order_selection_trims (_item, false);
3641 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3645 Drag::start_grab (event, cursor);
3648 case SelectionExtend:
3649 Drag::start_grab (event, cursor);
3653 if (_operation == SelectionMove) {
3654 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3656 show_verbose_cursor_time (adjusted_current_frame (event));
3659 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3663 SelectionDrag::setup_pointer_frame_offset ()
3665 switch (_operation) {
3666 case CreateSelection:
3667 _pointer_frame_offset = 0;
3670 case SelectionStartTrim:
3672 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3675 case SelectionEndTrim:
3676 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3679 case SelectionExtend:
3685 SelectionDrag::motion (GdkEvent* event, bool first_move)
3687 framepos_t start = 0;
3689 framecnt_t length = 0;
3690 framecnt_t distance = 0;
3692 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3693 if (pending_time_axis.first == 0) {
3697 framepos_t const pending_position = adjusted_current_frame (event);
3699 /* only alter selection if things have changed */
3701 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3705 switch (_operation) {
3706 case CreateSelection:
3708 framepos_t grab = grab_frame ();
3711 grab = adjusted_current_frame (event, false);
3712 if (grab < pending_position) {
3713 _editor->snap_to (grab, -1);
3715 _editor->snap_to (grab, 1);
3719 if (pending_position < grab) {
3720 start = pending_position;
3723 end = pending_position;
3727 /* first drag: Either add to the selection
3728 or create a new selection
3734 /* adding to the selection */
3735 _editor->set_selected_track_as_side_effect (Selection::Add);
3736 //_editor->selection->add (_editor->clicked_axisview);
3737 _editor->clicked_selection = _editor->selection->add (start, end);
3742 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3743 //_editor->selection->set (_editor->clicked_axisview);
3744 _editor->set_selected_track_as_side_effect (Selection::Set);
3747 _editor->clicked_selection = _editor->selection->set (start, end);
3751 /* select the track that we're in */
3752 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3753 // _editor->set_selected_track_as_side_effect (Selection::Add);
3754 _editor->selection->add (pending_time_axis.first);
3755 _added_time_axes.push_back (pending_time_axis.first);
3758 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3759 tracks that we selected in the first place.
3762 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3763 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3765 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3766 while (i != _added_time_axes.end()) {
3768 list<TimeAxisView*>::iterator tmp = i;
3771 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3772 _editor->selection->remove (*i);
3773 _added_time_axes.remove (*i);
3782 case SelectionStartTrim:
3784 start = _editor->selection->time[_editor->clicked_selection].start;
3785 end = _editor->selection->time[_editor->clicked_selection].end;
3787 if (pending_position > end) {
3790 start = pending_position;
3794 case SelectionEndTrim:
3796 start = _editor->selection->time[_editor->clicked_selection].start;
3797 end = _editor->selection->time[_editor->clicked_selection].end;
3799 if (pending_position < start) {
3802 end = pending_position;
3809 start = _editor->selection->time[_editor->clicked_selection].start;
3810 end = _editor->selection->time[_editor->clicked_selection].end;
3812 length = end - start;
3813 distance = pending_position - start;
3814 start = pending_position;
3815 _editor->snap_to (start);
3817 end = start + length;
3821 case SelectionExtend:
3825 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3826 _editor->start_canvas_autoscroll (1, 0);
3830 switch (_operation) {
3832 if (_time_selection_at_start) {
3833 _editor->selection->move_time (distance);
3837 _editor->selection->replace (_editor->clicked_selection, start, end);
3841 if (_operation == SelectionMove) {
3842 show_verbose_cursor_time(start);
3844 show_verbose_cursor_time(pending_position);
3849 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3851 Session* s = _editor->session();
3853 if (movement_occurred) {
3854 motion (event, false);
3855 /* XXX this is not object-oriented programming at all. ick */
3856 if (_editor->selection->time.consolidate()) {
3857 _editor->selection->TimeChanged ();
3860 /* XXX what if its a music time selection? */
3862 if ( s->get_play_range() && s->transport_rolling() ) {
3863 s->request_play_range (&_editor->selection->time, true);
3865 if (Config->get_always_play_range() && !s->transport_rolling()) {
3866 s->request_locate (_editor->get_selection().time.start());
3872 /* just a click, no pointer movement.
3875 if (_operation == SelectionExtend) {
3876 if (_time_selection_at_start) {
3877 framepos_t pos = adjusted_current_frame (event, false);
3878 framepos_t start = min (pos, start_at_start);
3879 framepos_t end = max (pos, end_at_start);
3880 _editor->selection->set (start, end);
3883 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3884 if (_editor->clicked_selection) {
3885 _editor->selection->remove (_editor->clicked_selection);
3888 if (!_editor->clicked_selection) {
3889 _editor->selection->clear_time();
3894 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3895 _editor->selection->set (_editor->clicked_axisview);
3898 if (s && s->get_play_range () && s->transport_rolling()) {
3899 s->request_stop (false, false);
3904 _editor->stop_canvas_autoscroll ();
3905 _editor->clicked_selection = 0;
3909 SelectionDrag::aborted (bool)
3914 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3919 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3921 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
3922 ArdourCanvas::Rect (0.0, 0.0, 0.0,
3923 physical_screen_height (_editor->get_window())));
3924 _drag_rect->hide ();
3926 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3927 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3931 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3933 if (_editor->session() == 0) {
3937 Gdk::Cursor* cursor = 0;
3939 if (!_editor->temp_location) {
3940 _editor->temp_location = new Location (*_editor->session());
3943 switch (_operation) {
3944 case CreateRangeMarker:
3945 case CreateTransportMarker:
3946 case CreateCDMarker:
3948 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3953 cursor = _editor->cursors()->selector;
3957 Drag::start_grab (event, cursor);
3959 show_verbose_cursor_time (adjusted_current_frame (event));
3963 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3965 framepos_t start = 0;
3967 ArdourCanvas::Rectangle *crect;
3969 switch (_operation) {
3970 case CreateRangeMarker:
3971 crect = _editor->range_bar_drag_rect;
3973 case CreateTransportMarker:
3974 crect = _editor->transport_bar_drag_rect;
3976 case CreateCDMarker:
3977 crect = _editor->cd_marker_bar_drag_rect;
3980 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
3985 framepos_t const pf = adjusted_current_frame (event);
3987 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3988 framepos_t grab = grab_frame ();
3989 _editor->snap_to (grab);
3991 if (pf < grab_frame()) {
3999 /* first drag: Either add to the selection
4000 or create a new selection.
4005 _editor->temp_location->set (start, end);
4009 update_item (_editor->temp_location);
4011 //_drag_rect->raise_to_top();
4016 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4017 _editor->start_canvas_autoscroll (1, 0);
4021 _editor->temp_location->set (start, end);
4023 double x1 = _editor->sample_to_pixel (start);
4024 double x2 = _editor->sample_to_pixel (end);
4028 update_item (_editor->temp_location);
4031 show_verbose_cursor_time (pf);
4036 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4038 Location * newloc = 0;
4042 if (movement_occurred) {
4043 motion (event, false);
4046 switch (_operation) {
4047 case CreateRangeMarker:
4048 case CreateCDMarker:
4050 _editor->begin_reversible_command (_("new range marker"));
4051 XMLNode &before = _editor->session()->locations()->get_state();
4052 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4053 if (_operation == CreateCDMarker) {
4054 flags = Location::IsRangeMarker | Location::IsCDMarker;
4055 _editor->cd_marker_bar_drag_rect->hide();
4058 flags = Location::IsRangeMarker;
4059 _editor->range_bar_drag_rect->hide();
4061 newloc = new Location (
4062 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4065 _editor->session()->locations()->add (newloc, true);
4066 XMLNode &after = _editor->session()->locations()->get_state();
4067 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4068 _editor->commit_reversible_command ();
4072 case CreateTransportMarker:
4073 // popup menu to pick loop or punch
4074 _editor->new_transport_marker_context_menu (&event->button, _item);
4078 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4080 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4085 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4087 if (end == max_framepos) {
4088 end = _editor->session()->current_end_frame ();
4091 if (start == max_framepos) {
4092 start = _editor->session()->current_start_frame ();
4095 switch (_editor->mouse_mode) {
4097 /* find the two markers on either side and then make the selection from it */
4098 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4102 /* find the two markers on either side of the click and make the range out of it */
4103 _editor->selection->set (start, end);
4112 _editor->stop_canvas_autoscroll ();
4116 RangeMarkerBarDrag::aborted (bool)
4122 RangeMarkerBarDrag::update_item (Location* location)
4124 double const x1 = _editor->sample_to_pixel (location->start());
4125 double const x2 = _editor->sample_to_pixel (location->end());
4127 _drag_rect->set_x0 (x1);
4128 _drag_rect->set_x1 (x2);
4131 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4135 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4139 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4141 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4142 Drag::start_grab (event, _editor->cursors()->zoom_out);
4145 Drag::start_grab (event, _editor->cursors()->zoom_in);
4149 show_verbose_cursor_time (adjusted_current_frame (event));
4153 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4158 framepos_t const pf = adjusted_current_frame (event);
4160 framepos_t grab = grab_frame ();
4161 _editor->snap_to_with_modifier (grab, event);
4163 /* base start and end on initial click position */
4175 _editor->zoom_rect->show();
4176 _editor->zoom_rect->raise_to_top();
4179 _editor->reposition_zoom_rect(start, end);
4181 show_verbose_cursor_time (pf);
4186 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4188 if (movement_occurred) {
4189 motion (event, false);
4191 if (grab_frame() < last_pointer_frame()) {
4192 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4194 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4197 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4198 _editor->tav_zoom_step (_zoom_out);
4200 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4204 _editor->zoom_rect->hide();
4208 MouseZoomDrag::aborted (bool)
4210 _editor->zoom_rect->hide ();
4213 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4215 , _cumulative_dx (0)
4216 , _cumulative_dy (0)
4218 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4220 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4222 _region = &_primary->region_view ();
4223 _note_height = _region->midi_stream_view()->note_height ();
4227 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4229 Drag::start_grab (event);
4231 if (!(_was_selected = _primary->selected())) {
4233 /* tertiary-click means extend selection - we'll do that on button release,
4234 so don't add it here, because otherwise we make it hard to figure
4235 out the "extend-to" range.
4238 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4241 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4244 _region->note_selected (_primary, true);
4246 _region->unique_select (_primary);
4252 /** @return Current total drag x change in frames */
4254 NoteDrag::total_dx () const
4257 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4259 /* primary note time */
4260 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4262 /* new time of the primary note in session frames */
4263 frameoffset_t st = n + dx;
4265 framepos_t const rp = _region->region()->position ();
4267 /* prevent the note being dragged earlier than the region's position */
4270 /* snap and return corresponding delta */
4271 return _region->snap_frame_to_frame (st - rp) + rp - n;
4274 /** @return Current total drag y change in note number */
4276 NoteDrag::total_dy () const
4278 MidiStreamView* msv = _region->midi_stream_view ();
4279 double const y = _region->midi_view()->y_position ();
4280 /* new current note */
4281 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4283 n = max (msv->lowest_note(), n);
4284 n = min (msv->highest_note(), n);
4285 /* and work out delta */
4286 return n - msv->y_to_note (grab_y() - y);
4290 NoteDrag::motion (GdkEvent *, bool)
4292 /* Total change in x and y since the start of the drag */
4293 frameoffset_t const dx = total_dx ();
4294 int8_t const dy = total_dy ();
4296 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4297 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4298 double const tdy = -dy * _note_height - _cumulative_dy;
4301 _cumulative_dx += tdx;
4302 _cumulative_dy += tdy;
4304 int8_t note_delta = total_dy();
4306 _region->move_selection (tdx, tdy, note_delta);
4308 /* the new note value may be the same as the old one, but we
4309 * don't know what that means because the selection may have
4310 * involved more than one note and we might be doing something
4311 * odd with them. so show the note value anyway, always.
4315 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4317 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4318 (int) floor (new_note));
4320 show_verbose_cursor_text (buf);
4325 NoteDrag::finished (GdkEvent* ev, bool moved)
4328 /* no motion - select note */
4330 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4331 _editor->current_mouse_mode() == Editing::MouseDraw) {
4333 if (_was_selected) {
4334 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4336 _region->note_deselected (_primary);
4339 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4340 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4342 if (!extend && !add && _region->selection_size() > 1) {
4343 _region->unique_select (_primary);
4344 } else if (extend) {
4345 _region->note_selected (_primary, true, true);
4347 /* it was added during button press */
4352 _region->note_dropped (_primary, total_dx(), total_dy());
4357 NoteDrag::aborted (bool)
4362 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4363 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4364 : Drag (editor, atv->base_item ())
4366 , _nothing_to_drag (false)
4368 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4369 y_origin = atv->y_position();
4370 setup (atv->lines ());
4373 /** Make an AutomationRangeDrag for region gain lines */
4374 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4375 : Drag (editor, rv->get_canvas_group ())
4377 , _nothing_to_drag (false)
4379 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4381 list<boost::shared_ptr<AutomationLine> > lines;
4382 lines.push_back (rv->get_gain_line ());
4383 y_origin = rv->get_time_axis_view().y_position();
4387 /** @param lines AutomationLines to drag.
4388 * @param offset Offset from the session start to the points in the AutomationLines.
4391 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4393 /* find the lines that overlap the ranges being dragged */
4394 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4395 while (i != lines.end ()) {
4396 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4399 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4401 /* check this range against all the AudioRanges that we are using */
4402 list<AudioRange>::const_iterator k = _ranges.begin ();
4403 while (k != _ranges.end()) {
4404 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4410 /* add it to our list if it overlaps at all */
4411 if (k != _ranges.end()) {
4416 _lines.push_back (n);
4422 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4426 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4428 return 1.0 - ((global_y - y_origin) / line->height());
4432 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4434 Drag::start_grab (event, cursor);
4436 /* Get line states before we start changing things */
4437 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4438 i->state = &i->line->get_state ();
4439 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4442 if (_ranges.empty()) {
4444 /* No selected time ranges: drag all points */
4445 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4446 uint32_t const N = i->line->npoints ();
4447 for (uint32_t j = 0; j < N; ++j) {
4448 i->points.push_back (i->line->nth (j));
4454 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4456 framecnt_t const half = (i->start + i->end) / 2;
4458 /* find the line that this audio range starts in */
4459 list<Line>::iterator j = _lines.begin();
4460 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4464 if (j != _lines.end()) {
4465 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4467 /* j is the line that this audio range starts in; fade into it;
4468 64 samples length plucked out of thin air.
4471 framepos_t a = i->start + 64;
4476 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4477 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4479 the_list->add (p, the_list->eval (p));
4480 the_list->add (q, the_list->eval (q));
4483 /* same thing for the end */
4486 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4490 if (j != _lines.end()) {
4491 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4493 /* j is the line that this audio range starts in; fade out of it;
4494 64 samples length plucked out of thin air.
4497 framepos_t b = i->end - 64;
4502 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4503 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4505 the_list->add (p, the_list->eval (p));
4506 the_list->add (q, the_list->eval (q));
4510 _nothing_to_drag = true;
4512 /* Find all the points that should be dragged and put them in the relevant
4513 points lists in the Line structs.
4516 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4518 uint32_t const N = i->line->npoints ();
4519 for (uint32_t j = 0; j < N; ++j) {
4521 /* here's a control point on this line */
4522 ControlPoint* p = i->line->nth (j);
4523 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4525 /* see if it's inside a range */
4526 list<AudioRange>::const_iterator k = _ranges.begin ();
4527 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4531 if (k != _ranges.end()) {
4532 /* dragging this point */
4533 _nothing_to_drag = false;
4534 i->points.push_back (p);
4540 if (_nothing_to_drag) {
4544 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4545 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4550 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4552 if (_nothing_to_drag) {
4556 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4557 float const f = y_fraction (l->line, _drags->current_pointer_y());
4558 /* we are ignoring x position for this drag, so we can just pass in anything */
4560 l->line->drag_motion (0, f, true, false, ignored);
4561 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4566 AutomationRangeDrag::finished (GdkEvent* event, bool)
4568 if (_nothing_to_drag) {
4572 motion (event, false);
4573 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4574 i->line->end_drag (false, 0);
4577 _editor->session()->commit_reversible_command ();
4581 AutomationRangeDrag::aborted (bool)
4583 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4588 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4591 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4592 layer = v->region()->layer ();
4593 initial_y = v->get_canvas_group()->position().y;
4594 initial_playlist = v->region()->playlist ();
4595 initial_position = v->region()->position ();
4596 initial_end = v->region()->position () + v->region()->length ();
4599 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4600 : Drag (e, i->canvas_item ())
4603 , _cumulative_dx (0)
4605 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4606 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4611 PatchChangeDrag::motion (GdkEvent* ev, bool)
4613 framepos_t f = adjusted_current_frame (ev);
4614 boost::shared_ptr<Region> r = _region_view->region ();
4615 f = max (f, r->position ());
4616 f = min (f, r->last_frame ());
4618 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4619 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4620 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4621 _cumulative_dx = dxu;
4625 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4627 if (!movement_occurred) {
4631 boost::shared_ptr<Region> r (_region_view->region ());
4632 framepos_t f = adjusted_current_frame (ev);
4633 f = max (f, r->position ());
4634 f = min (f, r->last_frame ());
4636 _region_view->move_patch_change (
4638 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4643 PatchChangeDrag::aborted (bool)
4645 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4649 PatchChangeDrag::setup_pointer_frame_offset ()
4651 boost::shared_ptr<Region> region = _region_view->region ();
4652 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4655 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4656 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4663 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4665 framepos_t const p = _region_view->region()->position ();
4666 double const y = _region_view->midi_view()->y_position ();
4668 x1 = max ((framepos_t) 0, x1 - p);
4669 x2 = max ((framepos_t) 0, x2 - p);
4670 y1 = max (0.0, y1 - y);
4671 y2 = max (0.0, y2 - y);
4673 _region_view->update_drag_selection (
4674 _editor->sample_to_pixel (x1),
4675 _editor->sample_to_pixel (x2),
4678 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4683 MidiRubberbandSelectDrag::deselect_things ()
4688 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4689 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4692 _vertical_only = true;
4696 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4698 double const y = _region_view->midi_view()->y_position ();
4700 y1 = max (0.0, y1 - y);
4701 y2 = max (0.0, y2 - y);
4703 _region_view->update_vertical_drag_selection (
4706 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4711 MidiVerticalSelectDrag::deselect_things ()
4716 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4717 : RubberbandSelectDrag (e, i)
4723 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4725 if (drag_in_progress) {
4726 /* We just want to select things at the end of the drag, not during it */
4730 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4732 _editor->begin_reversible_command (_("rubberband selection"));
4733 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4734 _editor->commit_reversible_command ();
4738 EditorRubberbandSelectDrag::deselect_things ()
4740 if (!getenv("ARDOUR_SAE")) {
4741 _editor->selection->clear_tracks();
4743 _editor->selection->clear_regions();
4744 _editor->selection->clear_points ();
4745 _editor->selection->clear_lines ();
4748 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4756 NoteCreateDrag::~NoteCreateDrag ()
4762 NoteCreateDrag::grid_frames (framepos_t t) const
4765 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4770 return _region_view->region_beats_to_region_frames (grid_beats);
4774 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4776 Drag::start_grab (event, cursor);
4778 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4780 framepos_t pf = _drags->current_pointer_frame ();
4781 framecnt_t const g = grid_frames (pf);
4783 /* Hack so that we always snap to the note that we are over, instead of snapping
4784 to the next one if we're more than halfway through the one we're over.
4786 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4790 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4792 MidiStreamView* sv = _region_view->midi_stream_view ();
4793 double const x = _editor->sample_to_pixel (_note[0]);
4794 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4796 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4797 _drag_rect->set_outline_what (0xff);
4798 _drag_rect->set_outline_color (0xffffff99);
4799 _drag_rect->set_fill_color (0xffffff66);
4803 NoteCreateDrag::motion (GdkEvent* event, bool)
4805 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4806 double const x = _editor->sample_to_pixel (_note[1]);
4807 if (_note[1] > _note[0]) {
4808 _drag_rect->set_x1 (x);
4810 _drag_rect->set_x0 (x);
4815 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4817 if (!had_movement) {
4821 framepos_t const start = min (_note[0], _note[1]);
4822 framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4824 framecnt_t const g = grid_frames (start);
4825 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4827 if (_editor->snap_mode() == SnapNormal && length < g) {
4828 length = g - one_tick;
4831 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4833 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4837 NoteCreateDrag::y_to_region (double y) const
4840 _region_view->get_canvas_group()->canvas_to_item (x, y);
4845 NoteCreateDrag::aborted (bool)
4850 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4855 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4859 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4861 Drag::start_grab (event, cursor);
4865 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4871 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4874 distance = _drags->current_pointer_x() - grab_x();
4875 len = ar->fade_in()->back()->when;
4877 distance = grab_x() - _drags->current_pointer_x();
4878 len = ar->fade_out()->back()->when;
4881 /* how long should it be ? */
4883 new_length = len + _editor->pixel_to_sample (distance);
4885 /* now check with the region that this is legal */
4887 new_length = ar->verify_xfade_bounds (new_length, start);
4890 arv->redraw_start_xfade_to (ar, new_length);
4892 arv->redraw_end_xfade_to (ar, new_length);
4897 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4903 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4906 distance = _drags->current_pointer_x() - grab_x();
4907 len = ar->fade_in()->back()->when;
4909 distance = grab_x() - _drags->current_pointer_x();
4910 len = ar->fade_out()->back()->when;
4913 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
4915 _editor->begin_reversible_command ("xfade trim");
4916 ar->playlist()->clear_owned_changes ();
4919 ar->set_fade_in_length (new_length);
4921 ar->set_fade_out_length (new_length);
4924 /* Adjusting the xfade may affect other regions in the playlist, so we need
4925 to get undo Commands from the whole playlist rather than just the
4929 vector<Command*> cmds;
4930 ar->playlist()->rdiff (cmds);
4931 _editor->session()->add_commands (cmds);
4932 _editor->commit_reversible_command ();
4937 CrossfadeEdgeDrag::aborted (bool)
4940 arv->redraw_start_xfade ();
4942 arv->redraw_end_xfade ();