2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
46 #include "audio_region_view.h"
47 #include "midi_region_view.h"
48 #include "ardour_ui.h"
49 #include "gui_thread.h"
50 #include "control_point.h"
52 #include "region_gain_line.h"
53 #include "editor_drag.h"
54 #include "audio_time_axis.h"
55 #include "midi_time_axis.h"
56 #include "selection.h"
57 #include "midi_selection.h"
58 #include "automation_time_axis.h"
60 #include "editor_cursors.h"
61 #include "mouse_cursors.h"
62 #include "note_base.h"
63 #include "patch_change.h"
64 #include "verbose_cursor.h"
67 using namespace ARDOUR;
70 using namespace Gtkmm2ext;
71 using namespace Editing;
72 using namespace ArdourCanvas;
74 using Gtkmm2ext::Keyboard;
76 double ControlPointDrag::_zero_gain_fraction = -1.0;
78 DragManager::DragManager (Editor* e)
81 , _current_pointer_frame (0)
85 DragManager::~DragManager ()
90 /** Call abort for each active drag */
96 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
101 if (!_drags.empty ()) {
102 _editor->set_follow_playhead (_old_follow_playhead, false);
111 DragManager::add (Drag* d)
113 d->set_manager (this);
114 _drags.push_back (d);
118 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
120 d->set_manager (this);
121 _drags.push_back (d);
126 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
128 /* Prevent follow playhead during the drag to be nice to the user */
129 _old_follow_playhead = _editor->follow_playhead ();
130 _editor->set_follow_playhead (false);
132 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
134 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
135 (*i)->start_grab (e, c);
139 /** Call end_grab for each active drag.
140 * @return true if any drag reported movement having occurred.
143 DragManager::end_grab (GdkEvent* e)
148 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
149 bool const t = (*i)->end_grab (e);
160 _editor->set_follow_playhead (_old_follow_playhead, false);
166 DragManager::mark_double_click ()
168 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
169 (*i)->set_double_click (true);
174 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
178 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
180 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
181 bool const t = (*i)->motion_handler (e, from_autoscroll);
192 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
196 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
198 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
199 bool const t = (*i)->motion_handler (e, from_autoscroll);
210 DragManager::have_item (ArdourCanvas::Item* i) const
212 list<Drag*>::const_iterator j = _drags.begin ();
213 while (j != _drags.end() && (*j)->item () != i) {
217 return j != _drags.end ();
220 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
223 , _pointer_frame_offset (0)
224 , _move_threshold_passed (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
234 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
247 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
249 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
251 if (Keyboard::is_button2_event (&event->button)) {
252 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
253 _y_constrained = true;
254 _x_constrained = false;
256 _y_constrained = false;
257 _x_constrained = true;
260 _x_constrained = false;
261 _y_constrained = false;
264 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
265 setup_pointer_frame_offset ();
266 _grab_frame = adjusted_frame (_raw_grab_frame, event);
267 _last_pointer_frame = _grab_frame;
268 _last_pointer_x = _grab_x;
269 _last_pointer_y = _grab_y;
275 /* CAIROCANVAS need a variant here that passes *cursor */
280 if (_editor->session() && _editor->session()->transport_rolling()) {
283 _was_rolling = false;
286 switch (_editor->snap_type()) {
287 case SnapToRegionStart:
288 case SnapToRegionEnd:
289 case SnapToRegionSync:
290 case SnapToRegionBoundary:
291 _editor->build_region_boundary_cache ();
298 /** Call to end a drag `successfully'. Ungrabs item and calls
299 * subclass' finished() method.
301 * @param event GDK event, or 0.
302 * @return true if some movement occurred, otherwise false.
305 Drag::end_grab (GdkEvent* event)
307 _editor->stop_canvas_autoscroll ();
311 finished (event, _move_threshold_passed);
313 _editor->verbose_cursor()->hide ();
315 return _move_threshold_passed;
319 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
323 if (f > _pointer_frame_offset) {
324 pos = f - _pointer_frame_offset;
328 _editor->snap_to_with_modifier (pos, event);
335 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
337 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
341 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
343 /* check to see if we have moved in any way that matters since the last motion event */
344 if (_move_threshold_passed &&
345 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
346 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
350 pair<framecnt_t, int> const threshold = move_threshold ();
352 bool const old_move_threshold_passed = _move_threshold_passed;
354 if (!from_autoscroll && !_move_threshold_passed) {
356 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
357 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
359 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
362 if (active (_editor->mouse_mode) && _move_threshold_passed) {
364 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
365 if (!from_autoscroll) {
366 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
369 if (!_editor->autoscroll_active() || from_autoscroll) {
370 motion (event, _move_threshold_passed != old_move_threshold_passed);
372 _last_pointer_x = _drags->current_pointer_x ();
373 _last_pointer_y = _drags->current_pointer_y ();
374 _last_pointer_frame = adjusted_current_frame (event);
383 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
391 aborted (_move_threshold_passed);
393 _editor->stop_canvas_autoscroll ();
394 _editor->verbose_cursor()->hide ();
398 Drag::show_verbose_cursor_time (framepos_t frame)
400 _editor->verbose_cursor()->set_time (
402 _drags->current_pointer_x() + 10,
403 _drags->current_pointer_y() + 10
406 _editor->verbose_cursor()->show ();
410 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
412 _editor->verbose_cursor()->show (xoffset);
414 _editor->verbose_cursor()->set_duration (
416 _drags->current_pointer_x() + 10,
417 _drags->current_pointer_y() + 10
422 Drag::show_verbose_cursor_text (string const & text)
424 _editor->verbose_cursor()->show ();
426 _editor->verbose_cursor()->set (
428 _drags->current_pointer_x() + 10,
429 _drags->current_pointer_y() + 10
433 boost::shared_ptr<Region>
434 Drag::add_midi_region (MidiTimeAxisView* view)
436 if (_editor->session()) {
437 const TempoMap& map (_editor->session()->tempo_map());
438 framecnt_t pos = grab_frame();
439 const Meter& m = map.meter_at (pos);
440 /* not that the frame rate used here can be affected by pull up/down which
443 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
444 return view->add_region (grab_frame(), len, true);
447 return boost::shared_ptr<Region>();
450 struct EditorOrderTimeAxisViewSorter {
451 bool operator() (TimeAxisView* a, TimeAxisView* b) {
452 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
453 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
455 return ra->route()->order_key () < rb->route()->order_key ();
459 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
463 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
465 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
466 as some of the regions we are dragging may be on such tracks.
469 TrackViewList track_views = _editor->track_views;
470 track_views.sort (EditorOrderTimeAxisViewSorter ());
472 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
473 _time_axis_views.push_back (*i);
475 TimeAxisView::Children children_list = (*i)->get_child_list ();
476 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
477 _time_axis_views.push_back (j->get());
481 /* the list of views can be empty at this point if this is a region list-insert drag
484 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
485 _views.push_back (DraggingView (*i, this));
488 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
492 RegionDrag::region_going_away (RegionView* v)
494 list<DraggingView>::iterator i = _views.begin ();
495 while (i != _views.end() && i->view != v) {
499 if (i != _views.end()) {
504 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
505 * or -1 if it is not found.
508 RegionDrag::find_time_axis_view (TimeAxisView* t) const
511 int const N = _time_axis_views.size ();
512 while (i < N && _time_axis_views[i] != t) {
523 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
524 : RegionDrag (e, i, p, v)
527 , _last_pointer_time_axis_view (0)
528 , _last_pointer_layer (0)
530 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
534 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
536 Drag::start_grab (event, cursor);
538 show_verbose_cursor_time (_last_frame_position);
540 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
542 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
543 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
548 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
550 /* compute the amount of pointer motion in frames, and where
551 the region would be if we moved it by that much.
553 *pending_region_position = adjusted_current_frame (event);
555 framepos_t sync_frame;
556 framecnt_t sync_offset;
559 sync_offset = _primary->region()->sync_offset (sync_dir);
561 /* we don't handle a sync point that lies before zero.
563 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
565 sync_frame = *pending_region_position + (sync_dir*sync_offset);
567 _editor->snap_to_with_modifier (sync_frame, event);
569 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
572 *pending_region_position = _last_frame_position;
575 if (*pending_region_position > max_framepos - _primary->region()->length()) {
576 *pending_region_position = _last_frame_position;
581 /* in locked edit mode, reverse the usual meaning of _x_constrained */
582 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
584 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
586 /* x movement since last time (in pixels) */
587 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
589 /* total x movement */
590 framecnt_t total_dx = *pending_region_position;
591 if (regions_came_from_canvas()) {
592 total_dx = total_dx - grab_frame ();
595 /* check that no regions have gone off the start of the session */
596 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
597 if ((i->view->region()->position() + total_dx) < 0) {
599 *pending_region_position = _last_frame_position;
604 _last_frame_position = *pending_region_position;
611 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
613 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
614 int const n = i->time_axis_view + delta_track;
615 if (n < 0 || n >= int (_time_axis_views.size())) {
616 /* off the top or bottom track */
620 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
621 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
622 /* not a track, or the wrong type */
626 double const l = i->layer + delta_layer;
628 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
629 mode to allow the user to place a region below another on layer 0.
631 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
632 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
633 If it has, the layers will be munged later anyway, so it's ok.
639 /* all regions being dragged are ok with this change */
644 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
646 double delta_layer = 0;
647 int delta_time_axis_view = 0;
649 assert (!_views.empty ());
651 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
653 /* Find the TimeAxisView that the pointer is now over */
654 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (_drags->current_pointer_y ());
655 TimeAxisView* tv = r.first;
659 double layer = r.second;
661 if (first_move && tv->view()->layer_display() == Stacked) {
662 tv->view()->set_layer_display (Expanded);
665 /* Here's the current pointer position in terms of time axis view and layer */
666 int const current_pointer_time_axis_view = find_time_axis_view (tv);
667 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
669 /* Work out the change in y */
671 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
672 delta_layer = current_pointer_layer - _last_pointer_layer;
675 /* Work out the change in x */
676 framepos_t pending_region_position;
677 double const x_delta = compute_x_delta (event, &pending_region_position);
679 /* Verify change in y */
680 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
681 /* this y movement is not allowed, so do no y movement this time */
682 delta_time_axis_view = 0;
686 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
687 /* haven't reached next snap point, and we're not switching
688 trackviews nor layers. nothing to do.
693 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
695 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
697 RegionView* rv = i->view;
699 if (rv->region()->locked() || rv->region()->video_locked()) {
707 /* Reparent to a non scrolling group so that we can keep the
708 region selection above all time axis views.
709 Reparenting means that we will have to move the region view
710 within its new parent, as the two parent groups have different coordinates.
713 ArdourCanvas::Group* rvg = rv->get_canvas_group();
714 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
716 rv->get_canvas_group()->reparent (_editor->_region_motion_group);
718 rv->fake_set_opaque (true);
719 rvg->set_position (rv_canvas_offset);
722 /* If we have moved tracks, we'll fudge the layer delta so that the
723 region gets moved back onto layer 0 on its new track; this avoids
724 confusion when dragging regions from non-zero layers onto different
727 double this_delta_layer = delta_layer;
728 if (delta_time_axis_view != 0) {
729 this_delta_layer = - i->layer;
734 /* The TimeAxisView that this region is now on */
735 TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
737 /* Ensure it is moved from stacked -> expanded if appropriate */
738 if (current_tv->view()->layer_display() == Stacked) {
739 current_tv->view()->set_layer_display (Expanded);
742 /* We're only allowed to go -ve in layer on Expanded views */
743 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
744 this_delta_layer = - i->layer;
748 rv->set_height (current_tv->view()->child_height ());
750 /* Update show/hidden status as the region view may have come from a hidden track,
751 or have moved to one.
753 if (current_tv->hidden ()) {
754 rv->get_canvas_group()->hide ();
756 rv->get_canvas_group()->show ();
759 /* Update the DraggingView */
760 i->time_axis_view += delta_time_axis_view;
761 i->layer += this_delta_layer;
764 _editor->mouse_brush_insert_region (rv, pending_region_position);
769 /* Get the y coordinate of the top of the track that this region is now on */
770 current_tv->canvas_display()->item_to_canvas (x, y);
772 /* And adjust for the layer that it should be on */
773 StreamView* cv = current_tv->view ();
774 switch (cv->layer_display ()) {
778 y += (cv->layers() - i->layer - 1) * cv->child_height ();
781 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
785 /* Now move the region view */
786 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
792 TimeAxisView* last = _time_axis_views.back();
793 last->canvas_display()->item_to_canvas (x, y);
794 y += last->effective_height();
795 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
796 i->time_axis_view = -1;
799 } /* foreach region */
801 _total_x_delta += x_delta;
803 if (x_delta != 0 && !_brushing) {
804 show_verbose_cursor_time (_last_frame_position);
807 _last_pointer_time_axis_view += delta_time_axis_view;
808 _last_pointer_layer += delta_layer;
812 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
814 if (_copy && first_move) {
816 /* duplicate the regionview(s) and region(s) */
818 list<DraggingView> new_regionviews;
820 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
822 RegionView* rv = i->view;
823 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
824 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
826 const boost::shared_ptr<const Region> original = rv->region();
827 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
828 region_copy->set_position (original->position());
832 boost::shared_ptr<AudioRegion> audioregion_copy
833 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
835 nrv = new AudioRegionView (*arv, audioregion_copy);
837 boost::shared_ptr<MidiRegion> midiregion_copy
838 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
839 nrv = new MidiRegionView (*mrv, midiregion_copy);
844 nrv->get_canvas_group()->show ();
845 new_regionviews.push_back (DraggingView (nrv, this));
847 /* swap _primary to the copy */
849 if (rv == _primary) {
853 /* ..and deselect the one we copied */
855 rv->set_selected (false);
858 if (!new_regionviews.empty()) {
860 /* reflect the fact that we are dragging the copies */
862 _views = new_regionviews;
864 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
868 RegionMotionDrag::motion (event, first_move);
872 RegionMotionDrag::finished (GdkEvent *, bool)
874 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
879 if ((*i)->view()->layer_display() == Expanded) {
880 (*i)->view()->set_layer_display (Stacked);
886 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
888 RegionMotionDrag::finished (ev, movement_occurred);
890 if (!movement_occurred) {
894 if (was_double_click() && !_views.empty()) {
895 DraggingView dv = _views.front();
896 dv.view->show_region_editor ();
903 /* reverse this here so that we have the correct logic to finalize
907 if (Config->get_edit_mode() == Lock) {
908 _x_constrained = !_x_constrained;
911 assert (!_views.empty ());
913 /* We might have hidden region views so that they weren't visible during the drag
914 (when they have been reparented). Now everything can be shown again, as region
915 views are back in their track parent groups.
917 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
918 i->view->get_canvas_group()->show ();
921 bool const changed_position = (_last_frame_position != _primary->region()->position());
922 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
923 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
943 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
947 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
949 RegionSelection new_views;
950 PlaylistSet modified_playlists;
951 list<RegionView*> views_to_delete;
954 /* all changes were made during motion event handlers */
956 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
960 _editor->commit_reversible_command ();
964 if (_x_constrained) {
965 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
967 _editor->begin_reversible_command (Operations::region_copy);
970 /* insert the regions into their new playlists */
971 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
973 if (i->view->region()->locked() || i->view->region()->video_locked()) {
979 if (changed_position && !_x_constrained) {
980 where = i->view->region()->position() - drag_delta;
982 where = i->view->region()->position();
985 RegionView* new_view = insert_region_into_playlist (
986 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
993 new_views.push_back (new_view);
995 /* we don't need the copied RegionView any more */
996 views_to_delete.push_back (i->view);
999 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
1000 because when views are deleted they are automagically removed from _views, which messes
1003 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
1007 /* If we've created new regions either by copying or moving
1008 to a new track, we want to replace the old selection with the new ones
1011 if (new_views.size() > 0) {
1012 _editor->selection->set (new_views);
1015 /* write commands for the accumulated diffs for all our modified playlists */
1016 add_stateful_diff_commands_for_playlists (modified_playlists);
1018 _editor->commit_reversible_command ();
1022 RegionMoveDrag::finished_no_copy (
1023 bool const changed_position,
1024 bool const changed_tracks,
1025 framecnt_t const drag_delta
1028 RegionSelection new_views;
1029 PlaylistSet modified_playlists;
1030 PlaylistSet frozen_playlists;
1031 set<RouteTimeAxisView*> views_to_update;
1032 RouteTimeAxisView* new_time_axis_view = 0;
1035 /* all changes were made during motion event handlers */
1036 _editor->commit_reversible_command ();
1040 if (_x_constrained) {
1041 _editor->begin_reversible_command (_("fixed time region drag"));
1043 _editor->begin_reversible_command (Operations::region_drag);
1046 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1048 RegionView* rv = i->view;
1049 RouteTimeAxisView* dest_rtv = 0;
1051 if (rv->region()->locked() || rv->region()->video_locked()) {
1056 if (i->time_axis_view < 0) {
1057 /* not over a time axis view */
1059 if (!new_time_axis_view) {
1061 /* Add a new track of the correct type, and use the new time axis view that is created when we do this
1062 as the destination for the region drop.
1066 if (boost::dynamic_pointer_cast<AudioRegion> (rv->region())) {
1067 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1068 audio_tracks = _editor->session()->new_audio_track (rv->region()->n_channels(), rv->region()->n_channels(), ARDOUR::Normal, 0, 1, rv->region()->name());
1069 new_time_axis_view = _editor->axis_view_from_route (audio_tracks.front());
1071 ChanCount one_midi_channel (DataType::MIDI, 1);
1072 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1073 midi_tracks = _editor->session()->new_midi_track (one_midi_channel, one_midi_channel, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, rv->region()->name());
1074 new_time_axis_view = _editor->axis_view_from_route (midi_tracks.front());
1077 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1082 dest_rtv = new_time_axis_view;
1084 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1089 double const dest_layer = i->layer;
1091 views_to_update.insert (dest_rtv);
1095 if (changed_position && !_x_constrained) {
1096 where = rv->region()->position() - drag_delta;
1098 where = rv->region()->position();
1101 if (changed_tracks) {
1103 /* insert into new playlist */
1105 RegionView* new_view = insert_region_into_playlist (
1106 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1109 if (new_view == 0) {
1114 new_views.push_back (new_view);
1116 /* remove from old playlist */
1118 /* the region that used to be in the old playlist is not
1119 moved to the new one - we use a copy of it. as a result,
1120 any existing editor for the region should no longer be
1123 rv->hide_region_editor();
1124 rv->fake_set_opaque (false);
1126 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1130 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1132 /* this movement may result in a crossfade being modified, or a layering change,
1133 so we need to get undo data from the playlist as well as the region.
1136 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1138 playlist->clear_changes ();
1141 rv->region()->clear_changes ();
1144 motion on the same track. plonk the previously reparented region
1145 back to its original canvas group (its streamview).
1146 No need to do anything for copies as they are fake regions which will be deleted.
1149 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1150 rv->get_canvas_group()->set_y_position (i->initial_y);
1153 /* just change the model */
1154 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1155 playlist->set_layer (rv->region(), dest_layer);
1158 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1160 r = frozen_playlists.insert (playlist);
1163 playlist->freeze ();
1166 rv->region()->set_position (where);
1168 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1171 if (changed_tracks) {
1173 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1174 was selected in all of them, then removing it from a playlist will have removed all
1175 trace of it from _views (i.e. there were N regions selected, we removed 1,
1176 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1177 corresponding regionview, and _views is now empty).
1179 This could have invalidated any and all iterators into _views.
1181 The heuristic we use here is: if the region selection is empty, break out of the loop
1182 here. if the region selection is not empty, then restart the loop because we know that
1183 we must have removed at least the region(view) we've just been working on as well as any
1184 that we processed on previous iterations.
1186 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1187 we can just iterate.
1191 if (_views.empty()) {
1202 /* If we've created new regions either by copying or moving
1203 to a new track, we want to replace the old selection with the new ones
1206 if (new_views.size() > 0) {
1207 _editor->selection->set (new_views);
1210 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1214 /* write commands for the accumulated diffs for all our modified playlists */
1215 add_stateful_diff_commands_for_playlists (modified_playlists);
1217 _editor->commit_reversible_command ();
1219 /* We have futzed with the layering of canvas items on our streamviews.
1220 If any region changed layer, this will have resulted in the stream
1221 views being asked to set up their region views, and all will be well.
1222 If not, we might now have badly-ordered region views. Ask the StreamViews
1223 involved to sort themselves out, just in case.
1226 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1227 (*i)->view()->playlist_layered ((*i)->track ());
1231 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1232 * @param region Region to remove.
1233 * @param playlist playlist To remove from.
1234 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1235 * that clear_changes () is only called once per playlist.
1238 RegionMoveDrag::remove_region_from_playlist (
1239 boost::shared_ptr<Region> region,
1240 boost::shared_ptr<Playlist> playlist,
1241 PlaylistSet& modified_playlists
1244 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1247 playlist->clear_changes ();
1250 playlist->remove_region (region);
1254 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1255 * clearing the playlist's diff history first if necessary.
1256 * @param region Region to insert.
1257 * @param dest_rtv Destination RouteTimeAxisView.
1258 * @param dest_layer Destination layer.
1259 * @param where Destination position.
1260 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1261 * that clear_changes () is only called once per playlist.
1262 * @return New RegionView, or 0 if no insert was performed.
1265 RegionMoveDrag::insert_region_into_playlist (
1266 boost::shared_ptr<Region> region,
1267 RouteTimeAxisView* dest_rtv,
1270 PlaylistSet& modified_playlists
1273 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1274 if (!dest_playlist) {
1278 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1279 _new_region_view = 0;
1280 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1282 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1283 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1285 dest_playlist->clear_changes ();
1288 dest_playlist->add_region (region, where);
1290 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1291 dest_playlist->set_layer (region, dest_layer);
1296 assert (_new_region_view);
1298 return _new_region_view;
1302 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1304 _new_region_view = rv;
1308 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1310 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1311 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1313 _editor->session()->add_command (c);
1322 RegionMoveDrag::aborted (bool movement_occurred)
1326 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1333 RegionMotionDrag::aborted (movement_occurred);
1338 RegionMotionDrag::aborted (bool)
1340 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1342 StreamView* sview = (*i)->view();
1345 if (sview->layer_display() == Expanded) {
1346 sview->set_layer_display (Stacked);
1351 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1352 RegionView* rv = i->view;
1353 TimeAxisView* tv = &(rv->get_time_axis_view ());
1354 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1356 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1357 rv->get_canvas_group()->set_y_position (0);
1359 rv->fake_set_opaque (false);
1360 rv->move (-_total_x_delta, 0);
1361 rv->set_height (rtv->view()->child_height ());
1365 /** @param b true to brush, otherwise false.
1366 * @param c true to make copies of the regions being moved, otherwise false.
1368 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1369 : RegionMotionDrag (e, i, p, v, b),
1372 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1375 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1376 if (rtv && rtv->is_track()) {
1377 speed = rtv->track()->speed ();
1380 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1384 RegionMoveDrag::setup_pointer_frame_offset ()
1386 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1389 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1390 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1392 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1394 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1395 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1397 _primary = v->view()->create_region_view (r, false, false);
1399 _primary->get_canvas_group()->show ();
1400 _primary->set_position (pos, 0);
1401 _views.push_back (DraggingView (_primary, this));
1403 _last_frame_position = pos;
1405 _item = _primary->get_canvas_group ();
1409 RegionInsertDrag::finished (GdkEvent *, bool)
1411 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1413 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1414 _primary->get_canvas_group()->set_y_position (0);
1416 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1418 _editor->begin_reversible_command (Operations::insert_region);
1419 playlist->clear_changes ();
1420 playlist->add_region (_primary->region (), _last_frame_position);
1421 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1422 _editor->commit_reversible_command ();
1430 RegionInsertDrag::aborted (bool)
1437 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1438 : RegionMoveDrag (e, i, p, v, false, false)
1440 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1443 struct RegionSelectionByPosition {
1444 bool operator() (RegionView*a, RegionView* b) {
1445 return a->region()->position () < b->region()->position();
1450 RegionSpliceDrag::motion (GdkEvent* event, bool)
1452 /* Which trackview is this ? */
1454 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1455 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1457 /* The region motion is only processed if the pointer is over
1461 if (!tv || !tv->is_track()) {
1462 /* To make sure we hide the verbose canvas cursor when the mouse is
1463 not held over and audiotrack.
1465 _editor->verbose_cursor()->hide ();
1471 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1477 RegionSelection copy (_editor->selection->regions);
1479 RegionSelectionByPosition cmp;
1482 framepos_t const pf = adjusted_current_frame (event);
1484 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1486 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1492 boost::shared_ptr<Playlist> playlist;
1494 if ((playlist = atv->playlist()) == 0) {
1498 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1503 if (pf < (*i)->region()->last_frame() + 1) {
1507 if (pf > (*i)->region()->first_frame()) {
1513 playlist->shuffle ((*i)->region(), dir);
1518 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1520 RegionMoveDrag::finished (event, movement_occurred);
1524 RegionSpliceDrag::aborted (bool)
1529 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1531 _view (dynamic_cast<MidiTimeAxisView*> (v))
1533 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1539 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1542 _region = add_midi_region (_view);
1543 _view->playlist()->freeze ();
1546 framepos_t const f = adjusted_current_frame (event);
1547 if (f < grab_frame()) {
1548 _region->set_position (f);
1551 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1552 so that if this region is duplicated, its duplicate starts on
1553 a snap point rather than 1 frame after a snap point. Otherwise things get
1554 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1555 place snapped notes at the start of the region.
1558 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1559 _region->set_length (len < 1 ? 1 : len);
1565 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1567 if (!movement_occurred) {
1568 add_midi_region (_view);
1570 _view->playlist()->thaw ();
1575 RegionCreateDrag::aborted (bool)
1578 _view->playlist()->thaw ();
1584 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1588 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1592 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1594 Gdk::Cursor* cursor;
1595 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1597 float x_fraction = cnote->mouse_x_fraction ();
1599 if (x_fraction > 0.0 && x_fraction < 0.25) {
1600 cursor = _editor->cursors()->left_side_trim;
1602 cursor = _editor->cursors()->right_side_trim;
1605 Drag::start_grab (event, cursor);
1607 region = &cnote->region_view();
1609 double const region_start = region->get_position_pixels();
1610 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1612 if (grab_x() <= middle_point) {
1613 cursor = _editor->cursors()->left_side_trim;
1616 cursor = _editor->cursors()->right_side_trim;
1622 if (event->motion.state & Keyboard::PrimaryModifier) {
1628 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1630 if (ms.size() > 1) {
1631 /* has to be relative, may make no sense otherwise */
1635 /* select this note; if it is already selected, preserve the existing selection,
1636 otherwise make this note the only one selected.
1638 region->note_selected (cnote, cnote->selected ());
1640 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1641 MidiRegionSelection::iterator next;
1644 (*r)->begin_resizing (at_front);
1650 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1652 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1653 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1654 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1656 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1661 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1663 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1664 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1665 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1667 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1672 NoteResizeDrag::aborted (bool)
1674 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1675 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1676 (*r)->abort_resizing ();
1680 AVDraggingView::AVDraggingView (RegionView* v)
1683 initial_position = v->region()->position ();
1686 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1689 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1692 TrackViewList empty;
1694 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1695 std::list<RegionView*> views = rs.by_layer();
1697 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1698 RegionView* rv = (*i);
1699 if (!rv->region()->video_locked()) {
1702 _views.push_back (AVDraggingView (rv));
1707 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1709 Drag::start_grab (event);
1710 if (_editor->session() == 0) {
1714 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1715 _max_backwards_drag = (
1716 ARDOUR_UI::instance()->video_timeline->get_duration()
1717 + ARDOUR_UI::instance()->video_timeline->get_offset()
1718 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1721 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1722 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1723 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1726 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1729 Timecode::Time timecode;
1730 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1731 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);
1732 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1733 _editor->verbose_cursor()->show ();
1737 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1739 if (_editor->session() == 0) {
1742 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1746 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1747 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1749 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1750 dt = - _max_backwards_drag;
1753 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1754 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1756 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1757 RegionView* rv = i->view;
1758 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1761 rv->fake_set_opaque (true);
1762 rv->region()->clear_changes ();
1763 rv->region()->suspend_property_changes();
1765 rv->region()->set_position(i->initial_position + dt);
1766 rv->region_changed(ARDOUR::Properties::position);
1769 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1770 Timecode::Time timecode;
1771 Timecode::Time timediff;
1773 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1774 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1775 snprintf (buf, sizeof (buf),
1776 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1777 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1778 , _("Video Start:"),
1779 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1781 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1783 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1784 _editor->verbose_cursor()->show ();
1788 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1790 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1794 if (!movement_occurred || ! _editor->session()) {
1798 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1800 _editor->begin_reversible_command (_("Move Video"));
1802 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1803 ARDOUR_UI::instance()->video_timeline->save_undo();
1804 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1805 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1807 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1808 i->view->drag_end();
1809 i->view->fake_set_opaque (false);
1810 i->view->region()->resume_property_changes ();
1812 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1815 _editor->session()->maybe_update_session_range(
1816 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1817 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1821 _editor->commit_reversible_command ();
1825 VideoTimeLineDrag::aborted (bool)
1827 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1830 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1831 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1833 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1834 i->view->region()->resume_property_changes ();
1835 i->view->region()->set_position(i->initial_position);
1839 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1840 : RegionDrag (e, i, p, v)
1841 , _preserve_fade_anchor (preserve_fade_anchor)
1842 , _jump_position_when_done (false)
1844 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1848 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1851 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1852 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1854 if (tv && tv->is_track()) {
1855 speed = tv->track()->speed();
1858 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1859 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1860 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1862 framepos_t const pf = adjusted_current_frame (event);
1864 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1865 /* Move the contents of the region around without changing the region bounds */
1866 _operation = ContentsTrim;
1867 Drag::start_grab (event, _editor->cursors()->trimmer);
1869 /* These will get overridden for a point trim.*/
1870 if (pf < (region_start + region_length/2)) {
1871 /* closer to front */
1872 _operation = StartTrim;
1873 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1876 _operation = EndTrim;
1877 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1881 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1882 _jump_position_when_done = true;
1885 switch (_operation) {
1887 show_verbose_cursor_time (region_start);
1888 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1889 i->view->trim_front_starting ();
1893 show_verbose_cursor_time (region_end);
1896 show_verbose_cursor_time (pf);
1900 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1901 i->view->region()->suspend_property_changes ();
1906 TrimDrag::motion (GdkEvent* event, bool first_move)
1908 RegionView* rv = _primary;
1911 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1912 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1913 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1914 frameoffset_t frame_delta = 0;
1916 if (tv && tv->is_track()) {
1917 speed = tv->track()->speed();
1920 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1926 switch (_operation) {
1928 trim_type = "Region start trim";
1931 trim_type = "Region end trim";
1934 trim_type = "Region content trim";
1938 _editor->begin_reversible_command (trim_type);
1940 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1941 RegionView* rv = i->view;
1942 rv->fake_set_opaque (false);
1943 rv->enable_display (false);
1944 rv->region()->playlist()->clear_owned_changes ();
1946 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1949 arv->temporarily_hide_envelope ();
1953 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1954 insert_result = _editor->motion_frozen_playlists.insert (pl);
1956 if (insert_result.second) {
1962 bool non_overlap_trim = false;
1964 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1965 non_overlap_trim = true;
1968 switch (_operation) {
1970 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1971 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1972 if (changed && _preserve_fade_anchor) {
1973 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1978 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1979 distance = _drags->current_pointer_x() - grab_x();
1980 len = ar->fade_in()->back()->when;
1981 new_length = len - _editor->pixel_to_sample (distance);
1982 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1983 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1990 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1991 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1992 if (changed && _preserve_fade_anchor) {
1993 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1998 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1999 distance = grab_x() - _drags->current_pointer_x();
2000 len = ar->fade_out()->back()->when;
2001 new_length = len - _editor->pixel_to_sample (distance);
2002 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2003 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
2011 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2013 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2014 i->view->move_contents (frame_delta);
2020 switch (_operation) {
2022 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2025 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2028 // show_verbose_cursor_time (frame_delta);
2035 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2037 if (movement_occurred) {
2038 motion (event, false);
2040 if (_operation == StartTrim) {
2041 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2043 /* This must happen before the region's StatefulDiffCommand is created, as it may
2044 `correct' (ahem) the region's _start from being negative to being zero. It
2045 needs to be zero in the undo record.
2047 i->view->trim_front_ending ();
2049 if (_preserve_fade_anchor) {
2050 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2055 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2056 distance = _drags->current_pointer_x() - grab_x();
2057 len = ar->fade_in()->back()->when;
2058 new_length = len - _editor->pixel_to_sample (distance);
2059 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2060 ar->set_fade_in_length(new_length);
2063 if (_jump_position_when_done) {
2064 i->view->region()->set_position (i->initial_position);
2067 } else if (_operation == EndTrim) {
2068 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2069 if (_preserve_fade_anchor) {
2070 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2075 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2076 distance = _drags->current_pointer_x() - grab_x();
2077 len = ar->fade_out()->back()->when;
2078 new_length = len - _editor->pixel_to_sample (distance);
2079 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2080 ar->set_fade_out_length(new_length);
2083 if (_jump_position_when_done) {
2084 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2089 if (!_views.empty()) {
2090 if (_operation == StartTrim) {
2091 _editor->maybe_locate_with_edit_preroll(
2092 _views.begin()->view->region()->position());
2094 if (_operation == EndTrim) {
2095 _editor->maybe_locate_with_edit_preroll(
2096 _views.begin()->view->region()->position() +
2097 _views.begin()->view->region()->length());
2101 if (!_editor->selection->selected (_primary)) {
2102 _primary->thaw_after_trim ();
2105 set<boost::shared_ptr<Playlist> > diffed_playlists;
2107 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2108 i->view->thaw_after_trim ();
2109 i->view->enable_display (true);
2110 i->view->fake_set_opaque (true);
2112 /* Trimming one region may affect others on the playlist, so we need
2113 to get undo Commands from the whole playlist rather than just the
2114 region. Use diffed_playlists to make sure we don't diff a given
2115 playlist more than once.
2117 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2118 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2119 vector<Command*> cmds;
2121 _editor->session()->add_commands (cmds);
2122 diffed_playlists.insert (p);
2127 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2131 _editor->motion_frozen_playlists.clear ();
2132 _editor->commit_reversible_command();
2135 /* no mouse movement */
2136 _editor->point_trim (event, adjusted_current_frame (event));
2139 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2140 if (_operation == StartTrim) {
2141 i->view->trim_front_ending ();
2144 i->view->region()->resume_property_changes ();
2149 TrimDrag::aborted (bool movement_occurred)
2151 /* Our motion method is changing model state, so use the Undo system
2152 to cancel. Perhaps not ideal, as this will leave an Undo point
2153 behind which may be slightly odd from the user's point of view.
2158 if (movement_occurred) {
2162 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2163 i->view->region()->resume_property_changes ();
2168 TrimDrag::setup_pointer_frame_offset ()
2170 list<DraggingView>::iterator i = _views.begin ();
2171 while (i != _views.end() && i->view != _primary) {
2175 if (i == _views.end()) {
2179 switch (_operation) {
2181 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2184 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2191 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2195 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2196 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2201 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2203 Drag::start_grab (event, cursor);
2204 show_verbose_cursor_time (adjusted_current_frame(event));
2208 MeterMarkerDrag::setup_pointer_frame_offset ()
2210 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2214 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2216 if (!_marker->meter().movable()) {
2222 // create a dummy marker for visual representation of moving the
2223 // section, because whether its a copy or not, we're going to
2224 // leave or lose the original marker (leave if its a copy; lose if its
2225 // not, because we'll remove it from the map).
2227 MeterSection section (_marker->meter());
2229 if (!section.movable()) {
2234 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2236 _marker = new MeterMarker (
2238 *_editor->meter_group,
2239 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2241 *new MeterSection (_marker->meter())
2244 /* use the new marker for the grab */
2245 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2248 TempoMap& map (_editor->session()->tempo_map());
2249 /* get current state */
2250 before_state = &map.get_state();
2251 /* remove the section while we drag it */
2252 map.remove_meter (section, true);
2256 framepos_t const pf = adjusted_current_frame (event);
2257 _marker->set_position (pf);
2258 show_verbose_cursor_time (pf);
2262 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2264 if (!movement_occurred) {
2265 if (was_double_click()) {
2266 _editor->edit_meter_marker (*_marker);
2271 if (!_marker->meter().movable()) {
2275 motion (event, false);
2277 Timecode::BBT_Time when;
2279 TempoMap& map (_editor->session()->tempo_map());
2280 map.bbt_time (last_pointer_frame(), when);
2282 if (_copy == true) {
2283 _editor->begin_reversible_command (_("copy meter mark"));
2284 XMLNode &before = map.get_state();
2285 map.add_meter (_marker->meter(), when);
2286 XMLNode &after = map.get_state();
2287 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2288 _editor->commit_reversible_command ();
2291 _editor->begin_reversible_command (_("move meter mark"));
2293 /* we removed it before, so add it back now */
2295 map.add_meter (_marker->meter(), when);
2296 XMLNode &after = map.get_state();
2297 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2298 _editor->commit_reversible_command ();
2301 // delete the dummy marker we used for visual representation while moving.
2302 // a new visual marker will show up automatically.
2307 MeterMarkerDrag::aborted (bool moved)
2309 _marker->set_position (_marker->meter().frame ());
2312 TempoMap& map (_editor->session()->tempo_map());
2313 /* we removed it before, so add it back now */
2314 map.add_meter (_marker->meter(), _marker->meter().frame());
2315 // delete the dummy marker we used for visual representation while moving.
2316 // a new visual marker will show up automatically.
2321 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2325 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2327 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2332 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2334 Drag::start_grab (event, cursor);
2335 show_verbose_cursor_time (adjusted_current_frame (event));
2339 TempoMarkerDrag::setup_pointer_frame_offset ()
2341 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2345 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2347 if (!_marker->tempo().movable()) {
2353 // create a dummy marker for visual representation of moving the
2354 // section, because whether its a copy or not, we're going to
2355 // leave or lose the original marker (leave if its a copy; lose if its
2356 // not, because we'll remove it from the map).
2358 // create a dummy marker for visual representation of moving the copy.
2359 // The actual copying is not done before we reach the finish callback.
2362 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2364 TempoSection section (_marker->tempo());
2366 _marker = new TempoMarker (
2368 *_editor->tempo_group,
2369 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2371 *new TempoSection (_marker->tempo())
2374 /* use the new marker for the grab */
2375 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2378 TempoMap& map (_editor->session()->tempo_map());
2379 /* get current state */
2380 before_state = &map.get_state();
2381 /* remove the section while we drag it */
2382 map.remove_tempo (section, true);
2386 framepos_t const pf = adjusted_current_frame (event);
2387 _marker->set_position (pf);
2388 show_verbose_cursor_time (pf);
2392 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2394 if (!movement_occurred) {
2395 if (was_double_click()) {
2396 _editor->edit_tempo_marker (*_marker);
2401 if (!_marker->tempo().movable()) {
2405 motion (event, false);
2407 TempoMap& map (_editor->session()->tempo_map());
2408 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2409 Timecode::BBT_Time when;
2411 map.bbt_time (beat_time, when);
2413 if (_copy == true) {
2414 _editor->begin_reversible_command (_("copy tempo mark"));
2415 XMLNode &before = map.get_state();
2416 map.add_tempo (_marker->tempo(), when);
2417 XMLNode &after = map.get_state();
2418 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2419 _editor->commit_reversible_command ();
2422 _editor->begin_reversible_command (_("move tempo mark"));
2423 /* we removed it before, so add it back now */
2424 map.add_tempo (_marker->tempo(), when);
2425 XMLNode &after = map.get_state();
2426 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2427 _editor->commit_reversible_command ();
2430 // delete the dummy marker we used for visual representation while moving.
2431 // a new visual marker will show up automatically.
2436 TempoMarkerDrag::aborted (bool moved)
2438 _marker->set_position (_marker->tempo().frame());
2440 TempoMap& map (_editor->session()->tempo_map());
2441 /* we removed it before, so add it back now */
2442 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2443 // delete the dummy marker we used for visual representation while moving.
2444 // a new visual marker will show up automatically.
2449 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2450 : Drag (e, &c.time_bar_canvas_item())
2454 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2457 /** Do all the things we do when dragging the playhead to make it look as though
2458 * we have located, without actually doing the locate (because that would cause
2459 * the diskstream buffers to be refilled, which is too slow).
2462 CursorDrag::fake_locate (framepos_t t)
2464 _editor->playhead_cursor->set_position (t);
2466 Session* s = _editor->session ();
2467 if (s->timecode_transmission_suspended ()) {
2468 framepos_t const f = _editor->playhead_cursor->current_frame ();
2469 /* This is asynchronous so it will be sent "now"
2471 s->send_mmc_locate (f);
2472 /* These are synchronous and will be sent during the next
2475 s->queue_full_time_code ();
2476 s->queue_song_position_pointer ();
2479 show_verbose_cursor_time (t);
2480 _editor->UpdateAllTransportClocks (t);
2484 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2486 Drag::start_grab (event, c);
2488 _grab_zoom = _editor->samples_per_pixel;
2490 framepos_t where = _editor->canvas_event_sample (event);
2492 _editor->snap_to_with_modifier (where, event);
2494 _editor->_dragging_playhead = true;
2496 Session* s = _editor->session ();
2498 /* grab the track canvas item as well */
2500 _cursor.track_canvas_item().grab();
2503 if (_was_rolling && _stop) {
2507 if (s->is_auditioning()) {
2508 s->cancel_audition ();
2512 if (AudioEngine::instance()->connected()) {
2514 /* do this only if we're the engine is connected
2515 * because otherwise this request will never be
2516 * serviced and we'll busy wait forever. likewise,
2517 * notice if we are disconnected while waiting for the
2518 * request to be serviced.
2521 s->request_suspend_timecode_transmission ();
2522 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2523 /* twiddle our thumbs */
2528 fake_locate (where);
2532 CursorDrag::motion (GdkEvent* event, bool)
2534 framepos_t const adjusted_frame = adjusted_current_frame (event);
2535 if (adjusted_frame != last_pointer_frame()) {
2536 fake_locate (adjusted_frame);
2541 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2543 _editor->_dragging_playhead = false;
2545 _cursor.track_canvas_item().ungrab();
2547 if (!movement_occurred && _stop) {
2551 motion (event, false);
2553 Session* s = _editor->session ();
2555 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2556 _editor->_pending_locate_request = true;
2557 s->request_resume_timecode_transmission ();
2562 CursorDrag::aborted (bool)
2564 _cursor.track_canvas_item().ungrab();
2566 if (_editor->_dragging_playhead) {
2567 _editor->session()->request_resume_timecode_transmission ();
2568 _editor->_dragging_playhead = false;
2571 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2574 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2575 : RegionDrag (e, i, p, v)
2577 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2581 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2583 Drag::start_grab (event, cursor);
2585 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2586 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2588 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2592 FadeInDrag::setup_pointer_frame_offset ()
2594 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2595 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2596 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2600 FadeInDrag::motion (GdkEvent* event, bool)
2602 framecnt_t fade_length;
2604 framepos_t const pos = adjusted_current_frame (event);
2606 boost::shared_ptr<Region> region = _primary->region ();
2608 if (pos < (region->position() + 64)) {
2609 fade_length = 64; // this should be a minimum defined somewhere
2610 } else if (pos > region->last_frame()) {
2611 fade_length = region->length();
2613 fade_length = pos - region->position();
2616 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2618 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2624 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2627 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2631 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2633 if (!movement_occurred) {
2637 framecnt_t fade_length;
2639 framepos_t const pos = adjusted_current_frame (event);
2641 boost::shared_ptr<Region> region = _primary->region ();
2643 if (pos < (region->position() + 64)) {
2644 fade_length = 64; // this should be a minimum defined somewhere
2645 } else if (pos > region->last_frame()) {
2646 fade_length = region->length();
2648 fade_length = pos - region->position();
2651 _editor->begin_reversible_command (_("change fade in length"));
2653 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2655 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2661 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2662 XMLNode &before = alist->get_state();
2664 tmp->audio_region()->set_fade_in_length (fade_length);
2665 tmp->audio_region()->set_fade_in_active (true);
2667 XMLNode &after = alist->get_state();
2668 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2671 _editor->commit_reversible_command ();
2675 FadeInDrag::aborted (bool)
2677 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2678 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2684 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2688 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2689 : RegionDrag (e, i, p, v)
2691 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2695 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2697 Drag::start_grab (event, cursor);
2699 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2700 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2702 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2706 FadeOutDrag::setup_pointer_frame_offset ()
2708 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2709 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2710 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2714 FadeOutDrag::motion (GdkEvent* event, bool)
2716 framecnt_t fade_length;
2718 framepos_t const pos = adjusted_current_frame (event);
2720 boost::shared_ptr<Region> region = _primary->region ();
2722 if (pos > (region->last_frame() - 64)) {
2723 fade_length = 64; // this should really be a minimum fade defined somewhere
2725 else if (pos < region->position()) {
2726 fade_length = region->length();
2729 fade_length = region->last_frame() - pos;
2732 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2734 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2740 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2743 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2747 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2749 if (!movement_occurred) {
2753 framecnt_t fade_length;
2755 framepos_t const pos = adjusted_current_frame (event);
2757 boost::shared_ptr<Region> region = _primary->region ();
2759 if (pos > (region->last_frame() - 64)) {
2760 fade_length = 64; // this should really be a minimum fade defined somewhere
2762 else if (pos < region->position()) {
2763 fade_length = region->length();
2766 fade_length = region->last_frame() - pos;
2769 _editor->begin_reversible_command (_("change fade out length"));
2771 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2773 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2779 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2780 XMLNode &before = alist->get_state();
2782 tmp->audio_region()->set_fade_out_length (fade_length);
2783 tmp->audio_region()->set_fade_out_active (true);
2785 XMLNode &after = alist->get_state();
2786 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2789 _editor->commit_reversible_command ();
2793 FadeOutDrag::aborted (bool)
2795 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2796 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2802 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2806 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2809 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2811 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2814 _points.push_back (ArdourCanvas::Duple (0, 0));
2815 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2818 MarkerDrag::~MarkerDrag ()
2820 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2825 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2827 location = new Location (*l);
2828 markers.push_back (m);
2833 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2835 Drag::start_grab (event, cursor);
2839 Location *location = _editor->find_location_from_marker (_marker, is_start);
2840 _editor->_dragging_edit_point = true;
2842 update_item (location);
2844 // _drag_line->show();
2845 // _line->raise_to_top();
2848 show_verbose_cursor_time (location->start());
2850 show_verbose_cursor_time (location->end());
2853 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2856 case Selection::Toggle:
2857 /* we toggle on the button release */
2859 case Selection::Set:
2860 if (!_editor->selection->selected (_marker)) {
2861 _editor->selection->set (_marker);
2864 case Selection::Extend:
2866 Locations::LocationList ll;
2867 list<Marker*> to_add;
2869 _editor->selection->markers.range (s, e);
2870 s = min (_marker->position(), s);
2871 e = max (_marker->position(), e);
2874 if (e < max_framepos) {
2877 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2878 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2879 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2882 to_add.push_back (lm->start);
2885 to_add.push_back (lm->end);
2889 if (!to_add.empty()) {
2890 _editor->selection->add (to_add);
2894 case Selection::Add:
2895 _editor->selection->add (_marker);
2899 /* Set up copies for us to manipulate during the drag
2902 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2904 Location* l = _editor->find_location_from_marker (*i, is_start);
2911 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2913 /* range: check that the other end of the range isn't
2916 CopiedLocationInfo::iterator x;
2917 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2918 if (*(*x).location == *l) {
2922 if (x == _copied_locations.end()) {
2923 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2925 (*x).markers.push_back (*i);
2926 (*x).move_both = true;
2934 MarkerDrag::setup_pointer_frame_offset ()
2937 Location *location = _editor->find_location_from_marker (_marker, is_start);
2938 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2942 MarkerDrag::motion (GdkEvent* event, bool)
2944 framecnt_t f_delta = 0;
2946 bool move_both = false;
2947 Location *real_location;
2948 Location *copy_location = 0;
2950 framepos_t const newframe = adjusted_current_frame (event);
2951 framepos_t next = newframe;
2953 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2957 CopiedLocationInfo::iterator x;
2959 /* find the marker we're dragging, and compute the delta */
2961 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2963 copy_location = (*x).location;
2965 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2967 /* this marker is represented by this
2968 * CopiedLocationMarkerInfo
2971 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2976 if (real_location->is_mark()) {
2977 f_delta = newframe - copy_location->start();
2981 switch (_marker->type()) {
2982 case Marker::SessionStart:
2983 case Marker::RangeStart:
2984 case Marker::LoopStart:
2985 case Marker::PunchIn:
2986 f_delta = newframe - copy_location->start();
2989 case Marker::SessionEnd:
2990 case Marker::RangeEnd:
2991 case Marker::LoopEnd:
2992 case Marker::PunchOut:
2993 f_delta = newframe - copy_location->end();
2996 /* what kind of marker is this ? */
3005 if (x == _copied_locations.end()) {
3006 /* hmm, impossible - we didn't find the dragged marker */
3010 /* now move them all */
3012 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3014 copy_location = x->location;
3016 /* call this to find out if its the start or end */
3018 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3022 if (real_location->locked()) {
3026 if (copy_location->is_mark()) {
3030 copy_location->set_start (copy_location->start() + f_delta);
3034 framepos_t new_start = copy_location->start() + f_delta;
3035 framepos_t new_end = copy_location->end() + f_delta;
3037 if (is_start) { // start-of-range marker
3039 if (move_both || (*x).move_both) {
3040 copy_location->set_start (new_start);
3041 copy_location->set_end (new_end);
3042 } else if (new_start < copy_location->end()) {
3043 copy_location->set_start (new_start);
3044 } else if (newframe > 0) {
3045 _editor->snap_to (next, 1, true);
3046 copy_location->set_end (next);
3047 copy_location->set_start (newframe);
3050 } else { // end marker
3052 if (move_both || (*x).move_both) {
3053 copy_location->set_end (new_end);
3054 copy_location->set_start (new_start);
3055 } else if (new_end > copy_location->start()) {
3056 copy_location->set_end (new_end);
3057 } else if (newframe > 0) {
3058 _editor->snap_to (next, -1, true);
3059 copy_location->set_start (next);
3060 copy_location->set_end (newframe);
3065 update_item (copy_location);
3067 /* now lookup the actual GUI items used to display this
3068 * location and move them to wherever the copy of the location
3069 * is now. This means that the logic in ARDOUR::Location is
3070 * still enforced, even though we are not (yet) modifying
3071 * the real Location itself.
3074 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3077 lm->set_position (copy_location->start(), copy_location->end());
3082 assert (!_copied_locations.empty());
3084 show_verbose_cursor_time (newframe);
3088 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3090 if (!movement_occurred) {
3092 if (was_double_click()) {
3093 _editor->rename_marker (_marker);
3097 /* just a click, do nothing but finish
3098 off the selection process
3101 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3104 case Selection::Set:
3105 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3106 _editor->selection->set (_marker);
3110 case Selection::Toggle:
3111 /* we toggle on the button release, click only */
3112 _editor->selection->toggle (_marker);
3115 case Selection::Extend:
3116 case Selection::Add:
3123 _editor->_dragging_edit_point = false;
3125 _editor->begin_reversible_command ( _("move marker") );
3126 XMLNode &before = _editor->session()->locations()->get_state();
3128 MarkerSelection::iterator i;
3129 CopiedLocationInfo::iterator x;
3132 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3133 x != _copied_locations.end() && i != _editor->selection->markers.end();
3136 Location * location = _editor->find_location_from_marker (*i, is_start);
3140 if (location->locked()) {
3144 if (location->is_mark()) {
3145 location->set_start (((*x).location)->start());
3147 location->set (((*x).location)->start(), ((*x).location)->end());
3152 XMLNode &after = _editor->session()->locations()->get_state();
3153 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3154 _editor->commit_reversible_command ();
3158 MarkerDrag::aborted (bool)
3164 MarkerDrag::update_item (Location*)
3169 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3171 _cumulative_x_drag (0),
3172 _cumulative_y_drag (0)
3174 if (_zero_gain_fraction < 0.0) {
3175 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3178 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3180 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3186 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3188 Drag::start_grab (event, _editor->cursors()->fader);
3190 // start the grab at the center of the control point so
3191 // the point doesn't 'jump' to the mouse after the first drag
3192 _fixed_grab_x = _point->get_x();
3193 _fixed_grab_y = _point->get_y();
3195 float const fraction = 1 - (_point->get_y() / _point->line().height());
3197 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3199 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3200 event->button.x + 10, event->button.y + 10);
3202 _editor->verbose_cursor()->show ();
3204 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3206 if (!_point->can_slide ()) {
3207 _x_constrained = true;
3212 ControlPointDrag::motion (GdkEvent* event, bool)
3214 double dx = _drags->current_pointer_x() - last_pointer_x();
3215 double dy = _drags->current_pointer_y() - last_pointer_y();
3217 if (event->button.state & Keyboard::SecondaryModifier) {
3222 /* coordinate in pixels relative to the start of the region (for region-based automation)
3223 or track (for track-based automation) */
3224 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3225 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3227 // calculate zero crossing point. back off by .01 to stay on the
3228 // positive side of zero
3229 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3231 // make sure we hit zero when passing through
3232 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3236 if (_x_constrained) {
3239 if (_y_constrained) {
3243 _cumulative_x_drag = cx - _fixed_grab_x;
3244 _cumulative_y_drag = cy - _fixed_grab_y;
3248 cy = min ((double) _point->line().height(), cy);
3250 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3252 if (!_x_constrained) {
3253 _editor->snap_to_with_modifier (cx_frames, event);
3256 cx_frames = min (cx_frames, _point->line().maximum_time());
3258 float const fraction = 1.0 - (cy / _point->line().height());
3260 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3262 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3266 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3268 if (!movement_occurred) {
3272 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3273 _editor->reset_point_selection ();
3277 motion (event, false);
3280 _point->line().end_drag (_pushing, _final_index);
3281 _editor->session()->commit_reversible_command ();
3285 ControlPointDrag::aborted (bool)
3287 _point->line().reset ();
3291 ControlPointDrag::active (Editing::MouseMode m)
3293 if (m == Editing::MouseGain) {
3294 /* always active in mouse gain */
3298 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3299 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3302 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3305 _cumulative_y_drag (0)
3307 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3311 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3313 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3316 _item = &_line->grab_item ();
3318 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3319 origin, and ditto for y.
3322 double cx = event->button.x;
3323 double cy = event->button.y;
3325 _line->parent_group().canvas_to_item (cx, cy);
3327 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3332 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3333 /* no adjacent points */
3337 Drag::start_grab (event, _editor->cursors()->fader);
3339 /* store grab start in parent frame */
3344 double fraction = 1.0 - (cy / _line->height());
3346 _line->start_drag_line (before, after, fraction);
3348 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3349 event->button.x + 10, event->button.y + 10);
3351 _editor->verbose_cursor()->show ();
3355 LineDrag::motion (GdkEvent* event, bool)
3357 double dy = _drags->current_pointer_y() - last_pointer_y();
3359 if (event->button.state & Keyboard::SecondaryModifier) {
3363 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3365 _cumulative_y_drag = cy - _fixed_grab_y;
3368 cy = min ((double) _line->height(), cy);
3370 double const fraction = 1.0 - (cy / _line->height());
3373 /* we are ignoring x position for this drag, so we can just pass in anything */
3374 _line->drag_motion (0, fraction, true, false, ignored);
3376 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3380 LineDrag::finished (GdkEvent* event, bool movement_occured)
3382 if (movement_occured) {
3383 motion (event, false);
3384 _line->end_drag (false, 0);
3386 /* add a new control point on the line */
3388 AutomationTimeAxisView* atv;
3390 _line->end_drag (false, 0);
3392 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3393 framepos_t where = _editor->window_event_sample (event, 0, 0);
3394 atv->add_automation_event (event, where, event->button.y, false);
3398 _editor->session()->commit_reversible_command ();
3402 LineDrag::aborted (bool)
3407 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3410 _cumulative_x_drag (0)
3412 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3416 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3418 Drag::start_grab (event);
3420 _line = reinterpret_cast<Line*> (_item);
3423 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3425 double cx = event->button.x;
3426 double cy = event->button.y;
3428 _item->parent()->canvas_to_item (cx, cy);
3430 /* store grab start in parent frame */
3431 _region_view_grab_x = cx;
3433 _before = *(float*) _item->get_data ("position");
3435 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3437 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3441 FeatureLineDrag::motion (GdkEvent*, bool)
3443 double dx = _drags->current_pointer_x() - last_pointer_x();
3445 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3447 _cumulative_x_drag += dx;
3449 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3458 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3460 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3462 float *pos = new float;
3465 _line->set_data ("position", pos);
3471 FeatureLineDrag::finished (GdkEvent*, bool)
3473 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3474 _arv->update_transient(_before, _before);
3478 FeatureLineDrag::aborted (bool)
3483 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3485 , _vertical_only (false)
3487 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3491 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3493 Drag::start_grab (event);
3494 show_verbose_cursor_time (adjusted_current_frame (event));
3498 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3505 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3507 framepos_t grab = grab_frame ();
3508 if (Config->get_rubberbanding_snaps_to_grid ()) {
3509 _editor->snap_to_with_modifier (grab, event);
3512 /* base start and end on initial click position */
3522 if (_drags->current_pointer_y() < grab_y()) {
3523 y1 = _drags->current_pointer_y();
3526 y2 = _drags->current_pointer_y();
3531 if (start != end || y1 != y2) {
3533 double x1 = _editor->sample_to_pixel (start);
3534 double x2 = _editor->sample_to_pixel (end);
3535 const double min_dimension = 2.0;
3537 _editor->rubberband_rect->set_x0 (x1);
3538 if (_vertical_only) {
3539 /* fixed 10 pixel width */
3540 _editor->rubberband_rect->set_x1 (x1 + 10);
3543 x2 = min (x1 - min_dimension, x2);
3545 x2 = max (x1 + min_dimension, x2);
3547 _editor->rubberband_rect->set_x1 (x2);
3550 _editor->rubberband_rect->set_y0 (y1);
3552 y2 = min (y1 - min_dimension, y2);
3554 y2 = max (y1 + min_dimension, y2);
3557 _editor->rubberband_rect->set_y1 (y2);
3559 _editor->rubberband_rect->show();
3560 _editor->rubberband_rect->raise_to_top();
3562 show_verbose_cursor_time (pf);
3564 do_select_things (event, true);
3569 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3574 if (grab_frame() < last_pointer_frame()) {
3576 x2 = last_pointer_frame ();
3579 x1 = last_pointer_frame ();
3585 if (_drags->current_pointer_y() < grab_y()) {
3586 y1 = _drags->current_pointer_y();
3589 y2 = _drags->current_pointer_y();
3593 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3597 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3599 if (movement_occurred) {
3601 motion (event, false);
3602 do_select_things (event, false);
3608 bool do_deselect = true;
3609 MidiTimeAxisView* mtv;
3611 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3613 if (_editor->selection->empty()) {
3614 /* nothing selected */
3615 add_midi_region (mtv);
3616 do_deselect = false;
3620 /* do not deselect if Primary or Tertiary (toggle-select or
3621 * extend-select are pressed.
3624 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3625 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3632 _editor->rubberband_rect->hide();
3636 RubberbandSelectDrag::aborted (bool)
3638 _editor->rubberband_rect->hide ();
3641 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3642 : RegionDrag (e, i, p, v)
3644 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3648 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3650 Drag::start_grab (event, cursor);
3652 show_verbose_cursor_time (adjusted_current_frame (event));
3656 TimeFXDrag::motion (GdkEvent* event, bool)
3658 RegionView* rv = _primary;
3659 StreamView* cv = rv->get_time_axis_view().view ();
3661 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3662 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3663 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3665 framepos_t const pf = adjusted_current_frame (event);
3667 if (pf > rv->region()->position()) {
3668 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3671 show_verbose_cursor_time (pf);
3675 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3677 _primary->get_time_axis_view().hide_timestretch ();
3679 if (!movement_occurred) {
3683 if (last_pointer_frame() < _primary->region()->position()) {
3684 /* backwards drag of the left edge - not usable */
3688 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3690 float percentage = (double) newlen / (double) _primary->region()->length();
3692 #ifndef USE_RUBBERBAND
3693 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3694 if (_primary->region()->data_type() == DataType::AUDIO) {
3695 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3699 if (!_editor->get_selection().regions.empty()) {
3700 /* primary will already be included in the selection, and edit
3701 group shared editing will propagate selection across
3702 equivalent regions, so just use the current region
3706 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3707 error << _("An error occurred while executing time stretch operation") << endmsg;
3713 TimeFXDrag::aborted (bool)
3715 _primary->get_time_axis_view().hide_timestretch ();
3718 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3721 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3725 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3727 Drag::start_grab (event);
3731 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3733 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3737 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3739 if (movement_occurred && _editor->session()) {
3740 /* make sure we stop */
3741 _editor->session()->request_transport_speed (0.0);
3746 ScrubDrag::aborted (bool)
3751 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3756 , _original_pointer_time_axis (-1)
3757 , _last_pointer_time_axis (-1)
3758 , _time_selection_at_start (!_editor->get_selection().time.empty())
3760 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3762 if (_time_selection_at_start) {
3763 start_at_start = _editor->get_selection().time.start();
3764 end_at_start = _editor->get_selection().time.end_frame();
3769 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3771 if (_editor->session() == 0) {
3775 Gdk::Cursor* cursor = 0;
3777 switch (_operation) {
3778 case CreateSelection:
3779 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3784 cursor = _editor->cursors()->selector;
3785 Drag::start_grab (event, cursor);
3788 case SelectionStartTrim:
3789 if (_editor->clicked_axisview) {
3790 _editor->clicked_axisview->order_selection_trims (_item, true);
3792 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3795 case SelectionEndTrim:
3796 if (_editor->clicked_axisview) {
3797 _editor->clicked_axisview->order_selection_trims (_item, false);
3799 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3803 Drag::start_grab (event, cursor);
3806 case SelectionExtend:
3807 Drag::start_grab (event, cursor);
3811 if (_operation == SelectionMove) {
3812 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3814 show_verbose_cursor_time (adjusted_current_frame (event));
3817 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3821 SelectionDrag::setup_pointer_frame_offset ()
3823 switch (_operation) {
3824 case CreateSelection:
3825 _pointer_frame_offset = 0;
3828 case SelectionStartTrim:
3830 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3833 case SelectionEndTrim:
3834 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3837 case SelectionExtend:
3843 SelectionDrag::motion (GdkEvent* event, bool first_move)
3845 framepos_t start = 0;
3847 framecnt_t length = 0;
3848 framecnt_t distance = 0;
3850 framepos_t const pending_position = adjusted_current_frame (event);
3852 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3856 switch (_operation) {
3857 case CreateSelection:
3859 framepos_t grab = grab_frame ();
3862 grab = adjusted_current_frame (event, false);
3863 if (grab < pending_position) {
3864 _editor->snap_to (grab, -1);
3866 _editor->snap_to (grab, 1);
3870 if (pending_position < grab) {
3871 start = pending_position;
3874 end = pending_position;
3878 /* first drag: Either add to the selection
3879 or create a new selection
3886 /* adding to the selection */
3887 _editor->set_selected_track_as_side_effect (Selection::Add);
3888 _editor->clicked_selection = _editor->selection->add (start, end);
3895 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3896 _editor->set_selected_track_as_side_effect (Selection::Set);
3899 _editor->clicked_selection = _editor->selection->set (start, end);
3903 /* select all tracks within the rectangle that we've marked out so far */
3904 TrackViewList to_be_added_to_selection;
3905 TrackViewList to_be_removed_from_selection;
3906 TrackViewList& all_tracks (_editor->track_views);
3908 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3910 if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
3911 if (!(*i)->get_selected()) {
3912 to_be_added_to_selection.push_back (*i);
3915 if ((*i)->get_selected()) {
3916 to_be_removed_from_selection.push_back (*i);
3921 if (!to_be_added_to_selection.empty()) {
3922 _editor->selection->add (to_be_added_to_selection);
3925 if (!to_be_removed_from_selection.empty()) {
3926 _editor->selection->remove (to_be_removed_from_selection);
3931 case SelectionStartTrim:
3933 start = _editor->selection->time[_editor->clicked_selection].start;
3934 end = _editor->selection->time[_editor->clicked_selection].end;
3936 if (pending_position > end) {
3939 start = pending_position;
3943 case SelectionEndTrim:
3945 start = _editor->selection->time[_editor->clicked_selection].start;
3946 end = _editor->selection->time[_editor->clicked_selection].end;
3948 if (pending_position < start) {
3951 end = pending_position;
3958 start = _editor->selection->time[_editor->clicked_selection].start;
3959 end = _editor->selection->time[_editor->clicked_selection].end;
3961 length = end - start;
3962 distance = pending_position - start;
3963 start = pending_position;
3964 _editor->snap_to (start);
3966 end = start + length;
3970 case SelectionExtend:
3974 _editor->maybe_autoscroll (true, false, false);
3977 switch (_operation) {
3979 if (_time_selection_at_start) {
3980 _editor->selection->move_time (distance);
3984 _editor->selection->replace (_editor->clicked_selection, start, end);
3988 if (_operation == SelectionMove) {
3989 show_verbose_cursor_time(start);
3991 show_verbose_cursor_time(pending_position);
3996 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3998 Session* s = _editor->session();
4000 if (movement_occurred) {
4001 motion (event, false);
4002 /* XXX this is not object-oriented programming at all. ick */
4003 if (_editor->selection->time.consolidate()) {
4004 _editor->selection->TimeChanged ();
4007 /* XXX what if its a music time selection? */
4009 if ( s->get_play_range() && s->transport_rolling() ) {
4010 s->request_play_range (&_editor->selection->time, true);
4012 if (Config->get_always_play_range() && !s->transport_rolling()) {
4013 s->request_locate (_editor->get_selection().time.start());
4019 /* just a click, no pointer movement.
4022 if (_operation == SelectionExtend) {
4023 if (_time_selection_at_start) {
4024 framepos_t pos = adjusted_current_frame (event, false);
4025 framepos_t start = min (pos, start_at_start);
4026 framepos_t end = max (pos, end_at_start);
4027 _editor->selection->set (start, end);
4030 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4031 if (_editor->clicked_selection) {
4032 _editor->selection->remove (_editor->clicked_selection);
4035 if (!_editor->clicked_selection) {
4036 _editor->selection->clear_time();
4041 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4042 _editor->selection->set (_editor->clicked_axisview);
4045 if (s && s->get_play_range () && s->transport_rolling()) {
4046 s->request_stop (false, false);
4051 _editor->stop_canvas_autoscroll ();
4052 _editor->clicked_selection = 0;
4056 SelectionDrag::aborted (bool)
4061 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4066 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4068 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4069 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4070 physical_screen_height (_editor->get_window())));
4071 _drag_rect->hide ();
4073 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4074 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4078 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4080 if (_editor->session() == 0) {
4084 Gdk::Cursor* cursor = 0;
4086 if (!_editor->temp_location) {
4087 _editor->temp_location = new Location (*_editor->session());
4090 switch (_operation) {
4091 case CreateRangeMarker:
4092 case CreateTransportMarker:
4093 case CreateCDMarker:
4095 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4100 cursor = _editor->cursors()->selector;
4104 Drag::start_grab (event, cursor);
4106 show_verbose_cursor_time (adjusted_current_frame (event));
4110 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4112 framepos_t start = 0;
4114 ArdourCanvas::Rectangle *crect;
4116 switch (_operation) {
4117 case CreateRangeMarker:
4118 crect = _editor->range_bar_drag_rect;
4120 case CreateTransportMarker:
4121 crect = _editor->transport_bar_drag_rect;
4123 case CreateCDMarker:
4124 crect = _editor->cd_marker_bar_drag_rect;
4127 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4132 framepos_t const pf = adjusted_current_frame (event);
4134 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4135 framepos_t grab = grab_frame ();
4136 _editor->snap_to (grab);
4138 if (pf < grab_frame()) {
4146 /* first drag: Either add to the selection
4147 or create a new selection.
4152 _editor->temp_location->set (start, end);
4156 update_item (_editor->temp_location);
4158 //_drag_rect->raise_to_top();
4163 _editor->maybe_autoscroll (true, false, false);
4166 _editor->temp_location->set (start, end);
4168 double x1 = _editor->sample_to_pixel (start);
4169 double x2 = _editor->sample_to_pixel (end);
4173 update_item (_editor->temp_location);
4176 show_verbose_cursor_time (pf);
4181 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4183 Location * newloc = 0;
4187 if (movement_occurred) {
4188 motion (event, false);
4191 switch (_operation) {
4192 case CreateRangeMarker:
4193 case CreateCDMarker:
4195 _editor->begin_reversible_command (_("new range marker"));
4196 XMLNode &before = _editor->session()->locations()->get_state();
4197 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4198 if (_operation == CreateCDMarker) {
4199 flags = Location::IsRangeMarker | Location::IsCDMarker;
4200 _editor->cd_marker_bar_drag_rect->hide();
4203 flags = Location::IsRangeMarker;
4204 _editor->range_bar_drag_rect->hide();
4206 newloc = new Location (
4207 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4210 _editor->session()->locations()->add (newloc, true);
4211 XMLNode &after = _editor->session()->locations()->get_state();
4212 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4213 _editor->commit_reversible_command ();
4217 case CreateTransportMarker:
4218 // popup menu to pick loop or punch
4219 _editor->new_transport_marker_context_menu (&event->button, _item);
4225 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4227 if (_operation == CreateTransportMarker) {
4229 /* didn't drag, so just locate */
4231 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4233 } else if (_operation == CreateCDMarker) {
4235 /* didn't drag, but mark is already created so do
4238 } else { /* operation == CreateRangeMarker */
4244 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4246 if (end == max_framepos) {
4247 end = _editor->session()->current_end_frame ();
4250 if (start == max_framepos) {
4251 start = _editor->session()->current_start_frame ();
4254 switch (_editor->mouse_mode) {
4256 /* find the two markers on either side and then make the selection from it */
4257 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4261 /* find the two markers on either side of the click and make the range out of it */
4262 _editor->selection->set (start, end);
4271 _editor->stop_canvas_autoscroll ();
4275 RangeMarkerBarDrag::aborted (bool)
4281 RangeMarkerBarDrag::update_item (Location* location)
4283 double const x1 = _editor->sample_to_pixel (location->start());
4284 double const x2 = _editor->sample_to_pixel (location->end());
4286 _drag_rect->set_x0 (x1);
4287 _drag_rect->set_x1 (x2);
4290 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4294 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4298 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4300 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4301 Drag::start_grab (event, _editor->cursors()->zoom_out);
4304 Drag::start_grab (event, _editor->cursors()->zoom_in);
4308 show_verbose_cursor_time (adjusted_current_frame (event));
4312 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4317 framepos_t const pf = adjusted_current_frame (event);
4319 framepos_t grab = grab_frame ();
4320 _editor->snap_to_with_modifier (grab, event);
4322 /* base start and end on initial click position */
4334 _editor->zoom_rect->show();
4335 _editor->zoom_rect->raise_to_top();
4338 _editor->reposition_zoom_rect(start, end);
4340 show_verbose_cursor_time (pf);
4345 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4347 if (movement_occurred) {
4348 motion (event, false);
4350 if (grab_frame() < last_pointer_frame()) {
4351 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4353 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4356 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4357 _editor->tav_zoom_step (_zoom_out);
4359 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4363 _editor->zoom_rect->hide();
4367 MouseZoomDrag::aborted (bool)
4369 _editor->zoom_rect->hide ();
4372 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4374 , _cumulative_dx (0)
4375 , _cumulative_dy (0)
4377 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4379 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4381 _region = &_primary->region_view ();
4382 _note_height = _region->midi_stream_view()->note_height ();
4386 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4388 Drag::start_grab (event);
4390 if (!(_was_selected = _primary->selected())) {
4392 /* tertiary-click means extend selection - we'll do that on button release,
4393 so don't add it here, because otherwise we make it hard to figure
4394 out the "extend-to" range.
4397 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4400 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4403 _region->note_selected (_primary, true);
4405 _region->unique_select (_primary);
4411 /** @return Current total drag x change in frames */
4413 NoteDrag::total_dx () const
4416 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4418 /* primary note time */
4419 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4421 /* new time of the primary note in session frames */
4422 frameoffset_t st = n + dx;
4424 framepos_t const rp = _region->region()->position ();
4426 /* prevent the note being dragged earlier than the region's position */
4429 /* snap and return corresponding delta */
4430 return _region->snap_frame_to_frame (st - rp) + rp - n;
4433 /** @return Current total drag y change in note number */
4435 NoteDrag::total_dy () const
4437 MidiStreamView* msv = _region->midi_stream_view ();
4438 double const y = _region->midi_view()->y_position ();
4439 /* new current note */
4440 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4442 n = max (msv->lowest_note(), n);
4443 n = min (msv->highest_note(), n);
4444 /* and work out delta */
4445 return n - msv->y_to_note (grab_y() - y);
4449 NoteDrag::motion (GdkEvent *, bool)
4451 /* Total change in x and y since the start of the drag */
4452 frameoffset_t const dx = total_dx ();
4453 int8_t const dy = total_dy ();
4455 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4456 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4457 double const tdy = -dy * _note_height - _cumulative_dy;
4460 _cumulative_dx += tdx;
4461 _cumulative_dy += tdy;
4463 int8_t note_delta = total_dy();
4465 _region->move_selection (tdx, tdy, note_delta);
4467 /* the new note value may be the same as the old one, but we
4468 * don't know what that means because the selection may have
4469 * involved more than one note and we might be doing something
4470 * odd with them. so show the note value anyway, always.
4474 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4476 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4477 (int) floor ((double)new_note));
4479 show_verbose_cursor_text (buf);
4484 NoteDrag::finished (GdkEvent* ev, bool moved)
4487 /* no motion - select note */
4489 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4490 _editor->current_mouse_mode() == Editing::MouseDraw) {
4492 if (_was_selected) {
4493 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4495 _region->note_deselected (_primary);
4498 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4499 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4501 if (!extend && !add && _region->selection_size() > 1) {
4502 _region->unique_select (_primary);
4503 } else if (extend) {
4504 _region->note_selected (_primary, true, true);
4506 /* it was added during button press */
4511 _region->note_dropped (_primary, total_dx(), total_dy());
4516 NoteDrag::aborted (bool)
4521 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4522 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4523 : Drag (editor, atv->base_item ())
4525 , _nothing_to_drag (false)
4527 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4528 y_origin = atv->y_position();
4529 setup (atv->lines ());
4532 /** Make an AutomationRangeDrag for region gain lines */
4533 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4534 : Drag (editor, rv->get_canvas_group ())
4536 , _nothing_to_drag (false)
4538 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4540 list<boost::shared_ptr<AutomationLine> > lines;
4541 lines.push_back (rv->get_gain_line ());
4542 y_origin = rv->get_time_axis_view().y_position();
4546 /** @param lines AutomationLines to drag.
4547 * @param offset Offset from the session start to the points in the AutomationLines.
4550 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4552 /* find the lines that overlap the ranges being dragged */
4553 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4554 while (i != lines.end ()) {
4555 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4558 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4560 /* check this range against all the AudioRanges that we are using */
4561 list<AudioRange>::const_iterator k = _ranges.begin ();
4562 while (k != _ranges.end()) {
4563 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4569 /* add it to our list if it overlaps at all */
4570 if (k != _ranges.end()) {
4575 _lines.push_back (n);
4581 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4585 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4587 return 1.0 - ((global_y - y_origin) / line->height());
4591 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4593 Drag::start_grab (event, cursor);
4595 /* Get line states before we start changing things */
4596 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4597 i->state = &i->line->get_state ();
4598 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4601 if (_ranges.empty()) {
4603 /* No selected time ranges: drag all points */
4604 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4605 uint32_t const N = i->line->npoints ();
4606 for (uint32_t j = 0; j < N; ++j) {
4607 i->points.push_back (i->line->nth (j));
4613 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4615 framecnt_t const half = (i->start + i->end) / 2;
4617 /* find the line that this audio range starts in */
4618 list<Line>::iterator j = _lines.begin();
4619 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4623 if (j != _lines.end()) {
4624 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4626 /* j is the line that this audio range starts in; fade into it;
4627 64 samples length plucked out of thin air.
4630 framepos_t a = i->start + 64;
4635 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4636 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4638 the_list->add (p, the_list->eval (p));
4639 the_list->add (q, the_list->eval (q));
4642 /* same thing for the end */
4645 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4649 if (j != _lines.end()) {
4650 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4652 /* j is the line that this audio range starts in; fade out of it;
4653 64 samples length plucked out of thin air.
4656 framepos_t b = i->end - 64;
4661 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4662 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4664 the_list->add (p, the_list->eval (p));
4665 the_list->add (q, the_list->eval (q));
4669 _nothing_to_drag = true;
4671 /* Find all the points that should be dragged and put them in the relevant
4672 points lists in the Line structs.
4675 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4677 uint32_t const N = i->line->npoints ();
4678 for (uint32_t j = 0; j < N; ++j) {
4680 /* here's a control point on this line */
4681 ControlPoint* p = i->line->nth (j);
4682 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4684 /* see if it's inside a range */
4685 list<AudioRange>::const_iterator k = _ranges.begin ();
4686 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4690 if (k != _ranges.end()) {
4691 /* dragging this point */
4692 _nothing_to_drag = false;
4693 i->points.push_back (p);
4699 if (_nothing_to_drag) {
4703 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4704 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4709 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4711 if (_nothing_to_drag) {
4715 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4716 float const f = y_fraction (l->line, _drags->current_pointer_y());
4717 /* we are ignoring x position for this drag, so we can just pass in anything */
4719 l->line->drag_motion (0, f, true, false, ignored);
4720 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4725 AutomationRangeDrag::finished (GdkEvent* event, bool)
4727 if (_nothing_to_drag) {
4731 motion (event, false);
4732 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4733 i->line->end_drag (false, 0);
4736 _editor->session()->commit_reversible_command ();
4740 AutomationRangeDrag::aborted (bool)
4742 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4747 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4750 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4751 layer = v->region()->layer ();
4752 initial_y = v->get_canvas_group()->position().y;
4753 initial_playlist = v->region()->playlist ();
4754 initial_position = v->region()->position ();
4755 initial_end = v->region()->position () + v->region()->length ();
4758 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4759 : Drag (e, i->canvas_item ())
4762 , _cumulative_dx (0)
4764 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4765 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4770 PatchChangeDrag::motion (GdkEvent* ev, bool)
4772 framepos_t f = adjusted_current_frame (ev);
4773 boost::shared_ptr<Region> r = _region_view->region ();
4774 f = max (f, r->position ());
4775 f = min (f, r->last_frame ());
4777 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4778 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4779 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4780 _cumulative_dx = dxu;
4784 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4786 if (!movement_occurred) {
4790 boost::shared_ptr<Region> r (_region_view->region ());
4791 framepos_t f = adjusted_current_frame (ev);
4792 f = max (f, r->position ());
4793 f = min (f, r->last_frame ());
4795 _region_view->move_patch_change (
4797 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4802 PatchChangeDrag::aborted (bool)
4804 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4808 PatchChangeDrag::setup_pointer_frame_offset ()
4810 boost::shared_ptr<Region> region = _region_view->region ();
4811 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4814 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4815 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4822 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4824 framepos_t const p = _region_view->region()->position ();
4825 double const y = _region_view->midi_view()->y_position ();
4827 x1 = max ((framepos_t) 0, x1 - p);
4828 x2 = max ((framepos_t) 0, x2 - p);
4829 y1 = max (0.0, y1 - y);
4830 y2 = max (0.0, y2 - y);
4832 _region_view->update_drag_selection (
4833 _editor->sample_to_pixel (x1),
4834 _editor->sample_to_pixel (x2),
4837 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4842 MidiRubberbandSelectDrag::deselect_things ()
4847 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4848 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4851 _vertical_only = true;
4855 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4857 double const y = _region_view->midi_view()->y_position ();
4859 y1 = max (0.0, y1 - y);
4860 y2 = max (0.0, y2 - y);
4862 _region_view->update_vertical_drag_selection (
4865 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4870 MidiVerticalSelectDrag::deselect_things ()
4875 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4876 : RubberbandSelectDrag (e, i)
4882 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4884 if (drag_in_progress) {
4885 /* We just want to select things at the end of the drag, not during it */
4889 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4891 _editor->begin_reversible_command (_("rubberband selection"));
4892 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4893 _editor->commit_reversible_command ();
4897 EditorRubberbandSelectDrag::deselect_things ()
4899 if (!getenv("ARDOUR_SAE")) {
4900 _editor->selection->clear_tracks();
4902 _editor->selection->clear_regions();
4903 _editor->selection->clear_points ();
4904 _editor->selection->clear_lines ();
4907 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4915 NoteCreateDrag::~NoteCreateDrag ()
4921 NoteCreateDrag::grid_frames (framepos_t t) const
4924 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4929 return _region_view->region_beats_to_region_frames (grid_beats);
4933 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4935 Drag::start_grab (event, cursor);
4937 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4939 framepos_t pf = _drags->current_pointer_frame ();
4940 framecnt_t const g = grid_frames (pf);
4942 /* Hack so that we always snap to the note that we are over, instead of snapping
4943 to the next one if we're more than halfway through the one we're over.
4945 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4949 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4951 MidiStreamView* sv = _region_view->midi_stream_view ();
4952 double const x = _editor->sample_to_pixel (_note[0]);
4953 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4955 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4956 _drag_rect->set_outline_all ();
4957 _drag_rect->set_outline_color (0xffffff99);
4958 _drag_rect->set_fill_color (0xffffff66);
4962 NoteCreateDrag::motion (GdkEvent* event, bool)
4964 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4965 double const x = _editor->sample_to_pixel (_note[1]);
4966 if (_note[1] > _note[0]) {
4967 _drag_rect->set_x1 (x);
4969 _drag_rect->set_x0 (x);
4974 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4976 if (!had_movement) {
4980 framepos_t const start = min (_note[0], _note[1]);
4981 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
4983 framecnt_t const g = grid_frames (start);
4984 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4986 if (_editor->snap_mode() == SnapNormal && length < g) {
4987 length = g - one_tick;
4990 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4992 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4996 NoteCreateDrag::y_to_region (double y) const
4999 _region_view->get_canvas_group()->canvas_to_item (x, y);
5004 NoteCreateDrag::aborted (bool)
5009 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5014 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5018 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5020 Drag::start_grab (event, cursor);
5024 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5030 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5033 distance = _drags->current_pointer_x() - grab_x();
5034 len = ar->fade_in()->back()->when;
5036 distance = grab_x() - _drags->current_pointer_x();
5037 len = ar->fade_out()->back()->when;
5040 /* how long should it be ? */
5042 new_length = len + _editor->pixel_to_sample (distance);
5044 /* now check with the region that this is legal */
5046 new_length = ar->verify_xfade_bounds (new_length, start);
5049 arv->reset_fade_in_shape_width (ar, new_length);
5051 arv->reset_fade_out_shape_width (ar, new_length);
5056 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5062 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5065 distance = _drags->current_pointer_x() - grab_x();
5066 len = ar->fade_in()->back()->when;
5068 distance = grab_x() - _drags->current_pointer_x();
5069 len = ar->fade_out()->back()->when;
5072 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5074 _editor->begin_reversible_command ("xfade trim");
5075 ar->playlist()->clear_owned_changes ();
5078 ar->set_fade_in_length (new_length);
5080 ar->set_fade_out_length (new_length);
5083 /* Adjusting the xfade may affect other regions in the playlist, so we need
5084 to get undo Commands from the whole playlist rather than just the
5088 vector<Command*> cmds;
5089 ar->playlist()->rdiff (cmds);
5090 _editor->session()->add_commands (cmds);
5091 _editor->commit_reversible_command ();
5096 CrossfadeEdgeDrag::aborted (bool)
5099 arv->redraw_start_xfade ();
5101 arv->redraw_end_xfade ();