2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/scroll_group.h"
48 #include "audio_region_view.h"
49 #include "midi_region_view.h"
50 #include "ardour_ui.h"
51 #include "gui_thread.h"
52 #include "control_point.h"
54 #include "region_gain_line.h"
55 #include "editor_drag.h"
56 #include "audio_time_axis.h"
57 #include "midi_time_axis.h"
58 #include "selection.h"
59 #include "midi_selection.h"
60 #include "automation_time_axis.h"
62 #include "editor_cursors.h"
63 #include "mouse_cursors.h"
64 #include "note_base.h"
65 #include "patch_change.h"
66 #include "verbose_cursor.h"
69 using namespace ARDOUR;
72 using namespace Gtkmm2ext;
73 using namespace Editing;
74 using namespace ArdourCanvas;
76 using Gtkmm2ext::Keyboard;
78 double ControlPointDrag::_zero_gain_fraction = -1.0;
80 DragManager::DragManager (Editor* e)
83 , _current_pointer_frame (0)
87 DragManager::~DragManager ()
92 /** Call abort for each active drag */
98 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
103 if (!_drags.empty ()) {
104 _editor->set_follow_playhead (_old_follow_playhead, false);
113 DragManager::add (Drag* d)
115 d->set_manager (this);
116 _drags.push_back (d);
120 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
122 d->set_manager (this);
123 _drags.push_back (d);
128 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
130 /* Prevent follow playhead during the drag to be nice to the user */
131 _old_follow_playhead = _editor->follow_playhead ();
132 _editor->set_follow_playhead (false);
134 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
136 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
137 (*i)->start_grab (e, c);
141 /** Call end_grab for each active drag.
142 * @return true if any drag reported movement having occurred.
145 DragManager::end_grab (GdkEvent* e)
150 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
151 bool const t = (*i)->end_grab (e);
162 _editor->set_follow_playhead (_old_follow_playhead, false);
168 DragManager::mark_double_click ()
170 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
171 (*i)->set_double_click (true);
176 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
180 /* calling this implies that we expect the event to have canvas
183 * Can we guarantee that this is true?
186 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
188 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
189 bool const t = (*i)->motion_handler (e, from_autoscroll);
190 /* run all handlers; return true if at least one of them
191 returns true (indicating that the event has been handled).
203 DragManager::have_item (ArdourCanvas::Item* i) const
205 list<Drag*>::const_iterator j = _drags.begin ();
206 while (j != _drags.end() && (*j)->item () != i) {
210 return j != _drags.end ();
213 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
216 , _pointer_frame_offset (0)
217 , _trackview_only (trackview_only)
218 , _move_threshold_passed (false)
219 , _was_double_click (false)
220 , _raw_grab_frame (0)
222 , _last_pointer_frame (0)
228 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
241 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
243 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
245 if (Keyboard::is_button2_event (&event->button)) {
246 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
247 _y_constrained = true;
248 _x_constrained = false;
250 _y_constrained = false;
251 _x_constrained = true;
254 _x_constrained = false;
255 _y_constrained = false;
258 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
259 setup_pointer_frame_offset ();
260 _grab_frame = adjusted_frame (_raw_grab_frame, event);
261 _last_pointer_frame = _grab_frame;
262 _last_pointer_x = _grab_x;
264 if (_trackview_only) {
265 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
268 _last_pointer_y = _grab_y;
273 /* CAIROCANVAS need a variant here that passes *cursor */
275 _editor->push_canvas_cursor (cursor);
278 if (_editor->session() && _editor->session()->transport_rolling()) {
281 _was_rolling = false;
284 switch (_editor->snap_type()) {
285 case SnapToRegionStart:
286 case SnapToRegionEnd:
287 case SnapToRegionSync:
288 case SnapToRegionBoundary:
289 _editor->build_region_boundary_cache ();
296 /** Call to end a drag `successfully'. Ungrabs item and calls
297 * subclass' finished() method.
299 * @param event GDK event, or 0.
300 * @return true if some movement occurred, otherwise false.
303 Drag::end_grab (GdkEvent* event)
305 _editor->stop_canvas_autoscroll ();
309 finished (event, _move_threshold_passed);
311 _editor->verbose_cursor()->hide ();
312 _editor->pop_canvas_cursor ();
314 return _move_threshold_passed;
318 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
322 if (f > _pointer_frame_offset) {
323 pos = f - _pointer_frame_offset;
327 _editor->snap_to_with_modifier (pos, event);
334 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
336 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
340 Drag::current_pointer_y () const
342 if (!_trackview_only) {
343 return _drags->current_pointer_y ();
346 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
350 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
352 /* check to see if we have moved in any way that matters since the last motion event */
353 if (_move_threshold_passed &&
354 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
355 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
359 pair<framecnt_t, int> const threshold = move_threshold ();
361 bool const old_move_threshold_passed = _move_threshold_passed;
363 if (!from_autoscroll && !_move_threshold_passed) {
365 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
366 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
368 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
371 if (active (_editor->mouse_mode) && _move_threshold_passed) {
373 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
374 if (!from_autoscroll) {
375 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
378 if (!_editor->autoscroll_active() || from_autoscroll) {
379 motion (event, _move_threshold_passed != old_move_threshold_passed);
381 _last_pointer_x = _drags->current_pointer_x ();
382 _last_pointer_y = current_pointer_y ();
383 _last_pointer_frame = adjusted_current_frame (event);
393 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
401 aborted (_move_threshold_passed);
403 _editor->stop_canvas_autoscroll ();
404 _editor->verbose_cursor()->hide ();
408 Drag::show_verbose_cursor_time (framepos_t frame)
410 /* We use DragManager::current_pointer_y() here
411 because we need to position the verbose canvas
412 cursor within the overall canvas, regardless
413 of this particular drag's _trackview_only
417 _editor->verbose_cursor()->set_time (
419 _drags->current_pointer_x() + 10,
420 _drags->current_pointer_y() + 10
423 _editor->verbose_cursor()->show ();
427 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
429 _editor->verbose_cursor()->show (xoffset);
431 /* We use DragManager::current_pointer_y() here
432 because we need to position the verbose canvas
433 cursor within the overall canvas, regardless
434 of this particular drag's _trackview_only
438 _editor->verbose_cursor()->set_duration (
440 _drags->current_pointer_x() + 10,
441 _drags->current_pointer_y() + 10
446 Drag::show_verbose_cursor_text (string const & text)
448 _editor->verbose_cursor()->show ();
450 /* We use DragManager::current_pointer_y() here
451 because we need to position the verbose canvas
452 cursor within the overall canvas, regardless
453 of this particular drag's _trackview_only
457 _editor->verbose_cursor()->set (
459 _drags->current_pointer_x() + 10,
460 _drags->current_pointer_y() + 10
464 boost::shared_ptr<Region>
465 Drag::add_midi_region (MidiTimeAxisView* view)
467 if (_editor->session()) {
468 const TempoMap& map (_editor->session()->tempo_map());
469 framecnt_t pos = grab_frame();
470 const Meter& m = map.meter_at (pos);
471 /* not that the frame rate used here can be affected by pull up/down which
474 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
475 return view->add_region (grab_frame(), len, true);
478 return boost::shared_ptr<Region>();
481 struct EditorOrderTimeAxisViewSorter {
482 bool operator() (TimeAxisView* a, TimeAxisView* b) {
483 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
484 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
486 return ra->route()->order_key () < rb->route()->order_key ();
490 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
494 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
496 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
497 as some of the regions we are dragging may be on such tracks.
500 TrackViewList track_views = _editor->track_views;
501 track_views.sort (EditorOrderTimeAxisViewSorter ());
503 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
504 _time_axis_views.push_back (*i);
506 TimeAxisView::Children children_list = (*i)->get_child_list ();
507 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
508 _time_axis_views.push_back (j->get());
512 /* the list of views can be empty at this point if this is a region list-insert drag
515 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
516 _views.push_back (DraggingView (*i, this));
519 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
523 RegionDrag::region_going_away (RegionView* v)
525 list<DraggingView>::iterator i = _views.begin ();
526 while (i != _views.end() && i->view != v) {
530 if (i != _views.end()) {
535 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
536 * or -1 if it is not found.
539 RegionDrag::find_time_axis_view (TimeAxisView* t) const
542 int const N = _time_axis_views.size ();
543 while (i < N && _time_axis_views[i] != t) {
554 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
555 : RegionDrag (e, i, p, v)
558 , _last_pointer_time_axis_view (0)
559 , _last_pointer_layer (0)
561 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
565 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
567 Drag::start_grab (event, cursor);
569 show_verbose_cursor_time (_last_frame_position);
571 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
573 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
574 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
579 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
581 /* compute the amount of pointer motion in frames, and where
582 the region would be if we moved it by that much.
584 *pending_region_position = adjusted_current_frame (event);
586 framepos_t sync_frame;
587 framecnt_t sync_offset;
590 sync_offset = _primary->region()->sync_offset (sync_dir);
592 /* we don't handle a sync point that lies before zero.
594 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
596 sync_frame = *pending_region_position + (sync_dir*sync_offset);
598 _editor->snap_to_with_modifier (sync_frame, event);
600 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
603 *pending_region_position = _last_frame_position;
606 if (*pending_region_position > max_framepos - _primary->region()->length()) {
607 *pending_region_position = _last_frame_position;
612 /* in locked edit mode, reverse the usual meaning of _x_constrained */
613 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
615 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
617 /* x movement since last time (in pixels) */
618 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
620 /* total x movement */
621 framecnt_t total_dx = *pending_region_position;
622 if (regions_came_from_canvas()) {
623 total_dx = total_dx - grab_frame ();
626 /* check that no regions have gone off the start of the session */
627 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
628 if ((i->view->region()->position() + total_dx) < 0) {
630 *pending_region_position = _last_frame_position;
635 _last_frame_position = *pending_region_position;
642 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
644 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
645 int const n = i->time_axis_view + delta_track;
646 if (n < 0 || n >= int (_time_axis_views.size())) {
647 /* off the top or bottom track */
651 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
652 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
653 /* not a track, or the wrong type */
657 double const l = i->layer + delta_layer;
659 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
660 mode to allow the user to place a region below another on layer 0.
662 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
663 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
664 If it has, the layers will be munged later anyway, so it's ok.
670 /* all regions being dragged are ok with this change */
675 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
677 double delta_layer = 0;
678 int delta_time_axis_view = 0;
680 assert (!_views.empty ());
682 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
684 /* Find the TimeAxisView that the pointer is now over */
685 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
686 TimeAxisView* tv = r.first;
688 if (tv && tv->view()) {
689 double layer = r.second;
691 if (first_move && tv->view()->layer_display() == Stacked) {
692 tv->view()->set_layer_display (Expanded);
695 /* Here's the current pointer position in terms of time axis view and layer */
696 int const current_pointer_time_axis_view = find_time_axis_view (tv);
697 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
699 /* Work out the change in y */
701 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
702 delta_layer = current_pointer_layer - _last_pointer_layer;
705 /* Work out the change in x */
706 framepos_t pending_region_position;
707 double const x_delta = compute_x_delta (event, &pending_region_position);
709 /* Verify change in y */
710 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
711 /* this y movement is not allowed, so do no y movement this time */
712 delta_time_axis_view = 0;
716 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
717 /* haven't reached next snap point, and we're not switching
718 trackviews nor layers. nothing to do.
723 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
725 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
727 RegionView* rv = i->view;
729 if (rv->region()->locked() || rv->region()->video_locked()) {
736 /* reparent the regionview into a group above all
740 ArdourCanvas::Group* rvg = rv->get_canvas_group();
741 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
742 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
743 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
744 /* move the item so that it continues to appear at the
745 same location now that its parent has changed.
747 rvg->move (rv_canvas_offset - dmg_canvas_offset);
750 /* If we have moved tracks, we'll fudge the layer delta so that the
751 region gets moved back onto layer 0 on its new track; this avoids
752 confusion when dragging regions from non-zero layers onto different
755 double this_delta_layer = delta_layer;
756 if (delta_time_axis_view != 0) {
757 this_delta_layer = - i->layer;
764 if (i->time_axis_view >= 0) {
765 track_index = i->time_axis_view + delta_time_axis_view;
767 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
770 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
774 /* The TimeAxisView that this region is now over */
775 TimeAxisView* current_tv = _time_axis_views[track_index];
777 /* Ensure it is moved from stacked -> expanded if appropriate */
778 if (current_tv->view()->layer_display() == Stacked) {
779 current_tv->view()->set_layer_display (Expanded);
782 /* We're only allowed to go -ve in layer on Expanded views */
783 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
784 this_delta_layer = - i->layer;
788 rv->set_height (current_tv->view()->child_height ());
790 /* Update show/hidden status as the region view may have come from a hidden track,
791 or have moved to one.
793 if (current_tv->hidden ()) {
794 rv->get_canvas_group()->hide ();
796 rv->get_canvas_group()->show ();
799 /* Update the DraggingView */
800 i->time_axis_view = track_index;
801 i->layer += this_delta_layer;
804 _editor->mouse_brush_insert_region (rv, pending_region_position);
808 /* Get the y coordinate of the top of the track that this region is now over */
809 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
811 /* And adjust for the layer that it should be on */
812 StreamView* cv = current_tv->view ();
813 switch (cv->layer_display ()) {
817 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
820 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
824 /* need to get the parent of the regionview
825 * canvas group and get its position in
826 * equivalent coordinate space as the trackview
827 * we are now dragging over.
830 /* Now move the region view */
831 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
835 /* Only move the region into the empty dropzone at the bottom if the pointer
839 if (current_pointer_y() >= 0) {
841 Coord last_track_bottom_edge;
842 if (!_time_axis_views.empty()) {
843 TimeAxisView* last = _time_axis_views.back();
844 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
846 last_track_bottom_edge = 0;
849 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
850 i->time_axis_view = -1;
854 } /* foreach region */
856 _total_x_delta += x_delta;
858 if (x_delta != 0 && !_brushing) {
859 show_verbose_cursor_time (_last_frame_position);
862 _last_pointer_time_axis_view += delta_time_axis_view;
863 _last_pointer_layer += delta_layer;
867 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
869 if (_copy && first_move) {
871 /* duplicate the regionview(s) and region(s) */
873 list<DraggingView> new_regionviews;
875 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
877 RegionView* rv = i->view;
878 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
879 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
881 const boost::shared_ptr<const Region> original = rv->region();
882 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
883 region_copy->set_position (original->position());
887 boost::shared_ptr<AudioRegion> audioregion_copy
888 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
890 nrv = new AudioRegionView (*arv, audioregion_copy);
892 boost::shared_ptr<MidiRegion> midiregion_copy
893 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
894 nrv = new MidiRegionView (*mrv, midiregion_copy);
899 nrv->get_canvas_group()->show ();
900 new_regionviews.push_back (DraggingView (nrv, this));
902 /* swap _primary to the copy */
904 if (rv == _primary) {
908 /* ..and deselect the one we copied */
910 rv->set_selected (false);
913 if (!new_regionviews.empty()) {
915 /* reflect the fact that we are dragging the copies */
917 _views = new_regionviews;
919 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
923 RegionMotionDrag::motion (event, first_move);
927 RegionMotionDrag::finished (GdkEvent *, bool)
929 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
934 if ((*i)->view()->layer_display() == Expanded) {
935 (*i)->view()->set_layer_display (Stacked);
941 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
943 RegionMotionDrag::finished (ev, movement_occurred);
945 if (!movement_occurred) {
949 if (was_double_click() && !_views.empty()) {
950 DraggingView dv = _views.front();
951 dv.view->show_region_editor ();
958 /* reverse this here so that we have the correct logic to finalize
962 if (Config->get_edit_mode() == Lock) {
963 _x_constrained = !_x_constrained;
966 assert (!_views.empty ());
968 /* We might have hidden region views so that they weren't visible during the drag
969 (when they have been reparented). Now everything can be shown again, as region
970 views are back in their track parent groups.
972 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
973 i->view->get_canvas_group()->show ();
976 bool const changed_position = (_last_frame_position != _primary->region()->position());
977 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
978 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
998 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1002 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
1004 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1009 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1010 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1011 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1012 return _editor->axis_view_from_route (audio_tracks.front());
1014 ChanCount one_midi_port (DataType::MIDI, 1);
1015 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1016 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1017 return _editor->axis_view_from_route (midi_tracks.front());
1020 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1026 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1028 RegionSelection new_views;
1029 PlaylistSet modified_playlists;
1030 RouteTimeAxisView* new_time_axis_view = 0;
1033 /* all changes were made during motion event handlers */
1035 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1039 _editor->commit_reversible_command ();
1043 if (_x_constrained) {
1044 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1046 _editor->begin_reversible_command (Operations::region_copy);
1049 /* insert the regions into their new playlists */
1050 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1052 RouteTimeAxisView* dest_rtv = 0;
1054 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1060 if (changed_position && !_x_constrained) {
1061 where = i->view->region()->position() - drag_delta;
1063 where = i->view->region()->position();
1066 if (i->time_axis_view < 0) {
1067 if (!new_time_axis_view) {
1068 new_time_axis_view = create_destination_time_axis (i->view->region());
1070 dest_rtv = new_time_axis_view;
1072 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1075 if (dest_rtv != 0) {
1076 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1077 if (new_view != 0) {
1078 new_views.push_back (new_view);
1082 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1083 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1086 list<DraggingView>::const_iterator next = i;
1092 /* If we've created new regions either by copying or moving
1093 to a new track, we want to replace the old selection with the new ones
1096 if (new_views.size() > 0) {
1097 _editor->selection->set (new_views);
1100 /* write commands for the accumulated diffs for all our modified playlists */
1101 add_stateful_diff_commands_for_playlists (modified_playlists);
1103 _editor->commit_reversible_command ();
1107 RegionMoveDrag::finished_no_copy (
1108 bool const changed_position,
1109 bool const changed_tracks,
1110 framecnt_t const drag_delta
1113 RegionSelection new_views;
1114 PlaylistSet modified_playlists;
1115 PlaylistSet frozen_playlists;
1116 set<RouteTimeAxisView*> views_to_update;
1117 RouteTimeAxisView* new_time_axis_view = 0;
1120 /* all changes were made during motion event handlers */
1121 _editor->commit_reversible_command ();
1125 if (_x_constrained) {
1126 _editor->begin_reversible_command (_("fixed time region drag"));
1128 _editor->begin_reversible_command (Operations::region_drag);
1131 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1133 RegionView* rv = i->view;
1134 RouteTimeAxisView* dest_rtv = 0;
1136 if (rv->region()->locked() || rv->region()->video_locked()) {
1141 if (i->time_axis_view < 0) {
1142 if (!new_time_axis_view) {
1143 new_time_axis_view = create_destination_time_axis (rv->region());
1145 dest_rtv = new_time_axis_view;
1147 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1152 double const dest_layer = i->layer;
1154 views_to_update.insert (dest_rtv);
1158 if (changed_position && !_x_constrained) {
1159 where = rv->region()->position() - drag_delta;
1161 where = rv->region()->position();
1164 if (changed_tracks) {
1166 /* insert into new playlist */
1168 RegionView* new_view = insert_region_into_playlist (
1169 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1172 if (new_view == 0) {
1177 new_views.push_back (new_view);
1179 /* remove from old playlist */
1181 /* the region that used to be in the old playlist is not
1182 moved to the new one - we use a copy of it. as a result,
1183 any existing editor for the region should no longer be
1186 rv->hide_region_editor();
1189 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1193 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1195 /* this movement may result in a crossfade being modified, or a layering change,
1196 so we need to get undo data from the playlist as well as the region.
1199 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1201 playlist->clear_changes ();
1204 rv->region()->clear_changes ();
1207 motion on the same track. plonk the previously reparented region
1208 back to its original canvas group (its streamview).
1209 No need to do anything for copies as they are fake regions which will be deleted.
1212 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1213 rv->get_canvas_group()->set_y_position (i->initial_y);
1216 /* just change the model */
1217 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1218 playlist->set_layer (rv->region(), dest_layer);
1221 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1223 r = frozen_playlists.insert (playlist);
1226 playlist->freeze ();
1229 rv->region()->set_position (where);
1231 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1234 if (changed_tracks) {
1236 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1237 was selected in all of them, then removing it from a playlist will have removed all
1238 trace of it from _views (i.e. there were N regions selected, we removed 1,
1239 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1240 corresponding regionview, and _views is now empty).
1242 This could have invalidated any and all iterators into _views.
1244 The heuristic we use here is: if the region selection is empty, break out of the loop
1245 here. if the region selection is not empty, then restart the loop because we know that
1246 we must have removed at least the region(view) we've just been working on as well as any
1247 that we processed on previous iterations.
1249 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1250 we can just iterate.
1254 if (_views.empty()) {
1265 /* If we've created new regions either by copying or moving
1266 to a new track, we want to replace the old selection with the new ones
1269 if (new_views.size() > 0) {
1270 _editor->selection->set (new_views);
1273 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1277 /* write commands for the accumulated diffs for all our modified playlists */
1278 add_stateful_diff_commands_for_playlists (modified_playlists);
1280 _editor->commit_reversible_command ();
1282 /* We have futzed with the layering of canvas items on our streamviews.
1283 If any region changed layer, this will have resulted in the stream
1284 views being asked to set up their region views, and all will be well.
1285 If not, we might now have badly-ordered region views. Ask the StreamViews
1286 involved to sort themselves out, just in case.
1289 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1290 (*i)->view()->playlist_layered ((*i)->track ());
1294 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1295 * @param region Region to remove.
1296 * @param playlist playlist To remove from.
1297 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1298 * that clear_changes () is only called once per playlist.
1301 RegionMoveDrag::remove_region_from_playlist (
1302 boost::shared_ptr<Region> region,
1303 boost::shared_ptr<Playlist> playlist,
1304 PlaylistSet& modified_playlists
1307 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1310 playlist->clear_changes ();
1313 playlist->remove_region (region);
1317 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1318 * clearing the playlist's diff history first if necessary.
1319 * @param region Region to insert.
1320 * @param dest_rtv Destination RouteTimeAxisView.
1321 * @param dest_layer Destination layer.
1322 * @param where Destination position.
1323 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1324 * that clear_changes () is only called once per playlist.
1325 * @return New RegionView, or 0 if no insert was performed.
1328 RegionMoveDrag::insert_region_into_playlist (
1329 boost::shared_ptr<Region> region,
1330 RouteTimeAxisView* dest_rtv,
1333 PlaylistSet& modified_playlists
1336 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1337 if (!dest_playlist) {
1341 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1342 _new_region_view = 0;
1343 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1345 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1346 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1348 dest_playlist->clear_changes ();
1351 dest_playlist->add_region (region, where);
1353 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1354 dest_playlist->set_layer (region, dest_layer);
1359 assert (_new_region_view);
1361 return _new_region_view;
1365 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1367 _new_region_view = rv;
1371 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1373 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1374 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1376 _editor->session()->add_command (c);
1385 RegionMoveDrag::aborted (bool movement_occurred)
1389 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1396 RegionMotionDrag::aborted (movement_occurred);
1401 RegionMotionDrag::aborted (bool)
1403 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1405 StreamView* sview = (*i)->view();
1408 if (sview->layer_display() == Expanded) {
1409 sview->set_layer_display (Stacked);
1414 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1415 RegionView* rv = i->view;
1416 TimeAxisView* tv = &(rv->get_time_axis_view ());
1417 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1419 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1420 rv->get_canvas_group()->set_y_position (0);
1422 rv->move (-_total_x_delta, 0);
1423 rv->set_height (rtv->view()->child_height ());
1427 /** @param b true to brush, otherwise false.
1428 * @param c true to make copies of the regions being moved, otherwise false.
1430 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1431 : RegionMotionDrag (e, i, p, v, b),
1434 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1437 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1438 if (rtv && rtv->is_track()) {
1439 speed = rtv->track()->speed ();
1442 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1446 RegionMoveDrag::setup_pointer_frame_offset ()
1448 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1451 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1452 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1454 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1456 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1457 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1459 _primary = v->view()->create_region_view (r, false, false);
1461 _primary->get_canvas_group()->show ();
1462 _primary->set_position (pos, 0);
1463 _views.push_back (DraggingView (_primary, this));
1465 _last_frame_position = pos;
1467 _item = _primary->get_canvas_group ();
1471 RegionInsertDrag::finished (GdkEvent *, bool)
1473 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1475 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1476 _primary->get_canvas_group()->set_y_position (0);
1478 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1480 _editor->begin_reversible_command (Operations::insert_region);
1481 playlist->clear_changes ();
1482 playlist->add_region (_primary->region (), _last_frame_position);
1483 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1484 _editor->commit_reversible_command ();
1492 RegionInsertDrag::aborted (bool)
1499 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1500 : RegionMoveDrag (e, i, p, v, false, false)
1502 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1505 struct RegionSelectionByPosition {
1506 bool operator() (RegionView*a, RegionView* b) {
1507 return a->region()->position () < b->region()->position();
1512 RegionSpliceDrag::motion (GdkEvent* event, bool)
1514 /* Which trackview is this ? */
1516 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1517 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1519 /* The region motion is only processed if the pointer is over
1523 if (!tv || !tv->is_track()) {
1524 /* To make sure we hide the verbose canvas cursor when the mouse is
1525 not held over and audiotrack.
1527 _editor->verbose_cursor()->hide ();
1533 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1539 RegionSelection copy (_editor->selection->regions);
1541 RegionSelectionByPosition cmp;
1544 framepos_t const pf = adjusted_current_frame (event);
1546 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1548 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1554 boost::shared_ptr<Playlist> playlist;
1556 if ((playlist = atv->playlist()) == 0) {
1560 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1565 if (pf < (*i)->region()->last_frame() + 1) {
1569 if (pf > (*i)->region()->first_frame()) {
1575 playlist->shuffle ((*i)->region(), dir);
1580 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1582 RegionMoveDrag::finished (event, movement_occurred);
1586 RegionSpliceDrag::aborted (bool)
1591 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1593 _view (dynamic_cast<MidiTimeAxisView*> (v))
1595 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1601 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1604 _region = add_midi_region (_view);
1605 _view->playlist()->freeze ();
1608 framepos_t const f = adjusted_current_frame (event);
1609 if (f < grab_frame()) {
1610 _region->set_position (f);
1613 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1614 so that if this region is duplicated, its duplicate starts on
1615 a snap point rather than 1 frame after a snap point. Otherwise things get
1616 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1617 place snapped notes at the start of the region.
1620 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1621 _region->set_length (len < 1 ? 1 : len);
1627 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1629 if (!movement_occurred) {
1630 add_midi_region (_view);
1632 _view->playlist()->thaw ();
1637 RegionCreateDrag::aborted (bool)
1640 _view->playlist()->thaw ();
1646 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1650 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1654 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1656 Gdk::Cursor* cursor;
1657 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1659 float x_fraction = cnote->mouse_x_fraction ();
1661 if (x_fraction > 0.0 && x_fraction < 0.25) {
1662 cursor = _editor->cursors()->left_side_trim;
1664 cursor = _editor->cursors()->right_side_trim;
1667 Drag::start_grab (event, cursor);
1669 region = &cnote->region_view();
1671 double const region_start = region->get_position_pixels();
1672 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1674 if (grab_x() <= middle_point) {
1675 cursor = _editor->cursors()->left_side_trim;
1678 cursor = _editor->cursors()->right_side_trim;
1684 if (event->motion.state & Keyboard::PrimaryModifier) {
1690 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1692 if (ms.size() > 1) {
1693 /* has to be relative, may make no sense otherwise */
1697 /* select this note; if it is already selected, preserve the existing selection,
1698 otherwise make this note the only one selected.
1700 region->note_selected (cnote, cnote->selected ());
1702 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1703 MidiRegionSelection::iterator next;
1706 (*r)->begin_resizing (at_front);
1712 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1714 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1715 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1716 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1718 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1723 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1725 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1726 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1727 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1729 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1734 NoteResizeDrag::aborted (bool)
1736 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1737 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1738 (*r)->abort_resizing ();
1742 AVDraggingView::AVDraggingView (RegionView* v)
1745 initial_position = v->region()->position ();
1748 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1751 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1754 TrackViewList empty;
1756 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1757 std::list<RegionView*> views = rs.by_layer();
1759 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1760 RegionView* rv = (*i);
1761 if (!rv->region()->video_locked()) {
1764 _views.push_back (AVDraggingView (rv));
1769 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1771 Drag::start_grab (event);
1772 if (_editor->session() == 0) {
1776 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1777 _max_backwards_drag = (
1778 ARDOUR_UI::instance()->video_timeline->get_duration()
1779 + ARDOUR_UI::instance()->video_timeline->get_offset()
1780 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1783 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1784 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1785 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1788 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1791 Timecode::Time timecode;
1792 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1793 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);
1794 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1795 _editor->verbose_cursor()->show ();
1799 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1801 if (_editor->session() == 0) {
1804 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1808 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1809 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1811 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1812 dt = - _max_backwards_drag;
1815 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1816 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1818 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1819 RegionView* rv = i->view;
1820 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1823 rv->region()->clear_changes ();
1824 rv->region()->suspend_property_changes();
1826 rv->region()->set_position(i->initial_position + dt);
1827 rv->region_changed(ARDOUR::Properties::position);
1830 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1831 Timecode::Time timecode;
1832 Timecode::Time timediff;
1834 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1835 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1836 snprintf (buf, sizeof (buf),
1837 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1838 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1839 , _("Video Start:"),
1840 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1842 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1844 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1845 _editor->verbose_cursor()->show ();
1849 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1851 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1855 if (!movement_occurred || ! _editor->session()) {
1859 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1861 _editor->begin_reversible_command (_("Move Video"));
1863 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1864 ARDOUR_UI::instance()->video_timeline->save_undo();
1865 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1866 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1868 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1869 i->view->drag_end();
1870 i->view->region()->resume_property_changes ();
1872 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1875 _editor->session()->maybe_update_session_range(
1876 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1877 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1881 _editor->commit_reversible_command ();
1885 VideoTimeLineDrag::aborted (bool)
1887 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1890 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1891 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1893 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1894 i->view->region()->resume_property_changes ();
1895 i->view->region()->set_position(i->initial_position);
1899 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1900 : RegionDrag (e, i, p, v)
1901 , _preserve_fade_anchor (preserve_fade_anchor)
1902 , _jump_position_when_done (false)
1904 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1908 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1911 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1912 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1914 if (tv && tv->is_track()) {
1915 speed = tv->track()->speed();
1918 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1919 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1920 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1922 framepos_t const pf = adjusted_current_frame (event);
1924 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1925 /* Move the contents of the region around without changing the region bounds */
1926 _operation = ContentsTrim;
1927 Drag::start_grab (event, _editor->cursors()->trimmer);
1929 /* These will get overridden for a point trim.*/
1930 if (pf < (region_start + region_length/2)) {
1931 /* closer to front */
1932 _operation = StartTrim;
1934 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1935 cerr << "start anchored leftdrag\n";
1936 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
1938 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1942 _operation = EndTrim;
1943 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1944 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
1945 cerr << "start anchored right drag\n";
1947 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1952 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1953 _jump_position_when_done = true;
1956 switch (_operation) {
1958 show_verbose_cursor_time (region_start);
1959 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1960 i->view->trim_front_starting ();
1964 show_verbose_cursor_time (region_end);
1967 show_verbose_cursor_time (pf);
1971 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1972 i->view->region()->suspend_property_changes ();
1977 TrimDrag::motion (GdkEvent* event, bool first_move)
1979 RegionView* rv = _primary;
1982 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1983 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1984 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1985 frameoffset_t frame_delta = 0;
1987 if (tv && tv->is_track()) {
1988 speed = tv->track()->speed();
1991 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1997 switch (_operation) {
1999 trim_type = "Region start trim";
2002 trim_type = "Region end trim";
2005 trim_type = "Region content trim";
2012 _editor->begin_reversible_command (trim_type);
2014 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2015 RegionView* rv = i->view;
2016 rv->enable_display (false);
2017 rv->region()->playlist()->clear_owned_changes ();
2019 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2022 arv->temporarily_hide_envelope ();
2026 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2027 insert_result = _editor->motion_frozen_playlists.insert (pl);
2029 if (insert_result.second) {
2035 bool non_overlap_trim = false;
2037 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2038 non_overlap_trim = true;
2041 /* contstrain trim to fade length */
2042 if (_preserve_fade_anchor) {
2043 switch (_operation) {
2045 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2046 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2048 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2049 if (ar->locked()) continue;
2050 framecnt_t len = ar->fade_in()->back()->when;
2051 if (len < dt) dt = min(dt, len);
2055 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2056 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2058 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2059 if (ar->locked()) continue;
2060 framecnt_t len = ar->fade_out()->back()->when;
2061 if (len < -dt) dt = max(dt, -len);
2070 switch (_operation) {
2072 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2073 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2074 if (changed && _preserve_fade_anchor) {
2075 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2077 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2078 framecnt_t len = ar->fade_in()->back()->when;
2079 framecnt_t diff = ar->first_frame() - i->initial_position;
2080 framepos_t new_length = len - diff;
2081 i->anchored_fade_length = min (ar->length(), new_length);
2082 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2083 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2090 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2091 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2092 if (changed && _preserve_fade_anchor) {
2093 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2095 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2096 framecnt_t len = ar->fade_out()->back()->when;
2097 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2098 framepos_t new_length = len + diff;
2099 i->anchored_fade_length = min (ar->length(), new_length);
2100 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2101 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2109 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2111 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2112 i->view->move_contents (frame_delta);
2118 switch (_operation) {
2120 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2123 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2126 // show_verbose_cursor_time (frame_delta);
2133 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2135 if (movement_occurred) {
2136 motion (event, false);
2138 if (_operation == StartTrim) {
2139 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2141 /* This must happen before the region's StatefulDiffCommand is created, as it may
2142 `correct' (ahem) the region's _start from being negative to being zero. It
2143 needs to be zero in the undo record.
2145 i->view->trim_front_ending ();
2147 if (_preserve_fade_anchor) {
2148 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2150 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2151 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2152 ar->set_fade_in_length(i->anchored_fade_length);
2153 ar->set_fade_in_active(true);
2156 if (_jump_position_when_done) {
2157 i->view->region()->set_position (i->initial_position);
2160 } else if (_operation == EndTrim) {
2161 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2162 if (_preserve_fade_anchor) {
2163 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2165 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2166 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2167 ar->set_fade_out_length(i->anchored_fade_length);
2168 ar->set_fade_out_active(true);
2171 if (_jump_position_when_done) {
2172 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2177 if (!_views.empty()) {
2178 if (_operation == StartTrim) {
2179 _editor->maybe_locate_with_edit_preroll(
2180 _views.begin()->view->region()->position());
2182 if (_operation == EndTrim) {
2183 _editor->maybe_locate_with_edit_preroll(
2184 _views.begin()->view->region()->position() +
2185 _views.begin()->view->region()->length());
2189 if (!_editor->selection->selected (_primary)) {
2190 _primary->thaw_after_trim ();
2193 set<boost::shared_ptr<Playlist> > diffed_playlists;
2195 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2196 i->view->thaw_after_trim ();
2197 i->view->enable_display (true);
2199 /* Trimming one region may affect others on the playlist, so we need
2200 to get undo Commands from the whole playlist rather than just the
2201 region. Use diffed_playlists to make sure we don't diff a given
2202 playlist more than once.
2204 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2205 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2206 vector<Command*> cmds;
2208 _editor->session()->add_commands (cmds);
2209 diffed_playlists.insert (p);
2214 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2218 _editor->motion_frozen_playlists.clear ();
2219 _editor->commit_reversible_command();
2222 /* no mouse movement */
2223 _editor->point_trim (event, adjusted_current_frame (event));
2226 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2227 if (_operation == StartTrim) {
2228 i->view->trim_front_ending ();
2231 i->view->region()->resume_property_changes ();
2236 TrimDrag::aborted (bool movement_occurred)
2238 /* Our motion method is changing model state, so use the Undo system
2239 to cancel. Perhaps not ideal, as this will leave an Undo point
2240 behind which may be slightly odd from the user's point of view.
2245 if (movement_occurred) {
2249 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2250 i->view->region()->resume_property_changes ();
2255 TrimDrag::setup_pointer_frame_offset ()
2257 list<DraggingView>::iterator i = _views.begin ();
2258 while (i != _views.end() && i->view != _primary) {
2262 if (i == _views.end()) {
2266 switch (_operation) {
2268 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2271 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2278 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2282 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2283 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2288 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2290 Drag::start_grab (event, cursor);
2291 show_verbose_cursor_time (adjusted_current_frame(event));
2295 MeterMarkerDrag::setup_pointer_frame_offset ()
2297 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2301 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2303 if (!_marker->meter().movable()) {
2309 // create a dummy marker for visual representation of moving the
2310 // section, because whether its a copy or not, we're going to
2311 // leave or lose the original marker (leave if its a copy; lose if its
2312 // not, because we'll remove it from the map).
2314 MeterSection section (_marker->meter());
2316 if (!section.movable()) {
2321 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2323 _marker = new MeterMarker (
2325 *_editor->meter_group,
2326 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2328 *new MeterSection (_marker->meter())
2331 /* use the new marker for the grab */
2332 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2335 TempoMap& map (_editor->session()->tempo_map());
2336 /* get current state */
2337 before_state = &map.get_state();
2338 /* remove the section while we drag it */
2339 map.remove_meter (section, true);
2343 framepos_t const pf = adjusted_current_frame (event);
2344 _marker->set_position (pf);
2345 show_verbose_cursor_time (pf);
2349 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2351 if (!movement_occurred) {
2352 if (was_double_click()) {
2353 _editor->edit_meter_marker (*_marker);
2358 if (!_marker->meter().movable()) {
2362 motion (event, false);
2364 Timecode::BBT_Time when;
2366 TempoMap& map (_editor->session()->tempo_map());
2367 map.bbt_time (last_pointer_frame(), when);
2369 if (_copy == true) {
2370 _editor->begin_reversible_command (_("copy meter mark"));
2371 XMLNode &before = map.get_state();
2372 map.add_meter (_marker->meter(), when);
2373 XMLNode &after = map.get_state();
2374 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2375 _editor->commit_reversible_command ();
2378 _editor->begin_reversible_command (_("move meter mark"));
2380 /* we removed it before, so add it back now */
2382 map.add_meter (_marker->meter(), when);
2383 XMLNode &after = map.get_state();
2384 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2385 _editor->commit_reversible_command ();
2388 // delete the dummy marker we used for visual representation while moving.
2389 // a new visual marker will show up automatically.
2394 MeterMarkerDrag::aborted (bool moved)
2396 _marker->set_position (_marker->meter().frame ());
2399 TempoMap& map (_editor->session()->tempo_map());
2400 /* we removed it before, so add it back now */
2401 map.add_meter (_marker->meter(), _marker->meter().frame());
2402 // delete the dummy marker we used for visual representation while moving.
2403 // a new visual marker will show up automatically.
2408 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2412 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2414 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2419 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2421 Drag::start_grab (event, cursor);
2422 show_verbose_cursor_time (adjusted_current_frame (event));
2426 TempoMarkerDrag::setup_pointer_frame_offset ()
2428 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2432 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2434 if (!_marker->tempo().movable()) {
2440 // create a dummy marker for visual representation of moving the
2441 // section, because whether its a copy or not, we're going to
2442 // leave or lose the original marker (leave if its a copy; lose if its
2443 // not, because we'll remove it from the map).
2445 // create a dummy marker for visual representation of moving the copy.
2446 // The actual copying is not done before we reach the finish callback.
2449 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2451 TempoSection section (_marker->tempo());
2453 _marker = new TempoMarker (
2455 *_editor->tempo_group,
2456 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2458 *new TempoSection (_marker->tempo())
2461 /* use the new marker for the grab */
2462 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2465 TempoMap& map (_editor->session()->tempo_map());
2466 /* get current state */
2467 before_state = &map.get_state();
2468 /* remove the section while we drag it */
2469 map.remove_tempo (section, true);
2473 framepos_t const pf = adjusted_current_frame (event);
2474 _marker->set_position (pf);
2475 show_verbose_cursor_time (pf);
2479 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2481 if (!movement_occurred) {
2482 if (was_double_click()) {
2483 _editor->edit_tempo_marker (*_marker);
2488 if (!_marker->tempo().movable()) {
2492 motion (event, false);
2494 TempoMap& map (_editor->session()->tempo_map());
2495 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2496 Timecode::BBT_Time when;
2498 map.bbt_time (beat_time, when);
2500 if (_copy == true) {
2501 _editor->begin_reversible_command (_("copy tempo mark"));
2502 XMLNode &before = map.get_state();
2503 map.add_tempo (_marker->tempo(), when);
2504 XMLNode &after = map.get_state();
2505 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2506 _editor->commit_reversible_command ();
2509 _editor->begin_reversible_command (_("move tempo mark"));
2510 /* we removed it before, so add it back now */
2511 map.add_tempo (_marker->tempo(), when);
2512 XMLNode &after = map.get_state();
2513 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2514 _editor->commit_reversible_command ();
2517 // delete the dummy marker we used for visual representation while moving.
2518 // a new visual marker will show up automatically.
2523 TempoMarkerDrag::aborted (bool moved)
2525 _marker->set_position (_marker->tempo().frame());
2527 TempoMap& map (_editor->session()->tempo_map());
2528 /* we removed it before, so add it back now */
2529 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2530 // delete the dummy marker we used for visual representation while moving.
2531 // a new visual marker will show up automatically.
2536 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2537 : Drag (e, &c.track_canvas_item(), false)
2541 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2544 /** Do all the things we do when dragging the playhead to make it look as though
2545 * we have located, without actually doing the locate (because that would cause
2546 * the diskstream buffers to be refilled, which is too slow).
2549 CursorDrag::fake_locate (framepos_t t)
2551 _editor->playhead_cursor->set_position (t);
2553 Session* s = _editor->session ();
2554 if (s->timecode_transmission_suspended ()) {
2555 framepos_t const f = _editor->playhead_cursor->current_frame ();
2556 /* This is asynchronous so it will be sent "now"
2558 s->send_mmc_locate (f);
2559 /* These are synchronous and will be sent during the next
2562 s->queue_full_time_code ();
2563 s->queue_song_position_pointer ();
2566 show_verbose_cursor_time (t);
2567 _editor->UpdateAllTransportClocks (t);
2571 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2573 Drag::start_grab (event, c);
2575 _grab_zoom = _editor->samples_per_pixel;
2577 framepos_t where = _editor->canvas_event_sample (event);
2579 _editor->snap_to_with_modifier (where, event);
2581 _editor->_dragging_playhead = true;
2583 Session* s = _editor->session ();
2585 /* grab the track canvas item as well */
2587 _cursor.track_canvas_item().grab();
2590 if (_was_rolling && _stop) {
2594 if (s->is_auditioning()) {
2595 s->cancel_audition ();
2599 if (AudioEngine::instance()->connected()) {
2601 /* do this only if we're the engine is connected
2602 * because otherwise this request will never be
2603 * serviced and we'll busy wait forever. likewise,
2604 * notice if we are disconnected while waiting for the
2605 * request to be serviced.
2608 s->request_suspend_timecode_transmission ();
2609 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2610 /* twiddle our thumbs */
2615 fake_locate (where);
2619 CursorDrag::motion (GdkEvent* event, bool)
2621 framepos_t const adjusted_frame = adjusted_current_frame (event);
2622 if (adjusted_frame != last_pointer_frame()) {
2623 fake_locate (adjusted_frame);
2628 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2630 _editor->_dragging_playhead = false;
2632 _cursor.track_canvas_item().ungrab();
2634 if (!movement_occurred && _stop) {
2638 motion (event, false);
2640 Session* s = _editor->session ();
2642 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2643 _editor->_pending_locate_request = true;
2644 s->request_resume_timecode_transmission ();
2649 CursorDrag::aborted (bool)
2651 _cursor.track_canvas_item().ungrab();
2653 if (_editor->_dragging_playhead) {
2654 _editor->session()->request_resume_timecode_transmission ();
2655 _editor->_dragging_playhead = false;
2658 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2661 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2662 : RegionDrag (e, i, p, v)
2664 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2668 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2670 Drag::start_grab (event, cursor);
2672 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2673 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2675 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2679 FadeInDrag::setup_pointer_frame_offset ()
2681 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2682 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2683 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2687 FadeInDrag::motion (GdkEvent* event, bool)
2689 framecnt_t fade_length;
2691 framepos_t const pos = adjusted_current_frame (event);
2693 boost::shared_ptr<Region> region = _primary->region ();
2695 if (pos < (region->position() + 64)) {
2696 fade_length = 64; // this should be a minimum defined somewhere
2697 } else if (pos > region->last_frame()) {
2698 fade_length = region->length();
2700 fade_length = pos - region->position();
2703 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2705 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2711 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2714 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2718 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2720 if (!movement_occurred) {
2724 framecnt_t fade_length;
2726 framepos_t const pos = adjusted_current_frame (event);
2728 boost::shared_ptr<Region> region = _primary->region ();
2730 if (pos < (region->position() + 64)) {
2731 fade_length = 64; // this should be a minimum defined somewhere
2732 } else if (pos > region->last_frame()) {
2733 fade_length = region->length();
2735 fade_length = pos - region->position();
2738 _editor->begin_reversible_command (_("change fade in length"));
2740 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2742 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2748 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2749 XMLNode &before = alist->get_state();
2751 tmp->audio_region()->set_fade_in_length (fade_length);
2752 tmp->audio_region()->set_fade_in_active (true);
2754 XMLNode &after = alist->get_state();
2755 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2758 _editor->commit_reversible_command ();
2762 FadeInDrag::aborted (bool)
2764 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2765 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2771 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2775 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2776 : RegionDrag (e, i, p, v)
2778 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2782 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2784 Drag::start_grab (event, cursor);
2786 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2787 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2789 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2793 FadeOutDrag::setup_pointer_frame_offset ()
2795 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2796 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2797 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2801 FadeOutDrag::motion (GdkEvent* event, bool)
2803 framecnt_t fade_length;
2805 framepos_t const pos = adjusted_current_frame (event);
2807 boost::shared_ptr<Region> region = _primary->region ();
2809 if (pos > (region->last_frame() - 64)) {
2810 fade_length = 64; // this should really be a minimum fade defined somewhere
2812 else if (pos < region->position()) {
2813 fade_length = region->length();
2816 fade_length = region->last_frame() - pos;
2819 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2821 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2827 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2830 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2834 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2836 if (!movement_occurred) {
2840 framecnt_t fade_length;
2842 framepos_t const pos = adjusted_current_frame (event);
2844 boost::shared_ptr<Region> region = _primary->region ();
2846 if (pos > (region->last_frame() - 64)) {
2847 fade_length = 64; // this should really be a minimum fade defined somewhere
2849 else if (pos < region->position()) {
2850 fade_length = region->length();
2853 fade_length = region->last_frame() - pos;
2856 _editor->begin_reversible_command (_("change fade out length"));
2858 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2860 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2866 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2867 XMLNode &before = alist->get_state();
2869 tmp->audio_region()->set_fade_out_length (fade_length);
2870 tmp->audio_region()->set_fade_out_active (true);
2872 XMLNode &after = alist->get_state();
2873 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2876 _editor->commit_reversible_command ();
2880 FadeOutDrag::aborted (bool)
2882 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2883 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2889 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2893 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2896 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2898 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2901 _points.push_back (ArdourCanvas::Duple (0, 0));
2902 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2905 MarkerDrag::~MarkerDrag ()
2907 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2912 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2914 location = new Location (*l);
2915 markers.push_back (m);
2920 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2922 Drag::start_grab (event, cursor);
2926 Location *location = _editor->find_location_from_marker (_marker, is_start);
2927 _editor->_dragging_edit_point = true;
2929 update_item (location);
2931 // _drag_line->show();
2932 // _line->raise_to_top();
2935 show_verbose_cursor_time (location->start());
2937 show_verbose_cursor_time (location->end());
2940 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2943 case Selection::Toggle:
2944 /* we toggle on the button release */
2946 case Selection::Set:
2947 if (!_editor->selection->selected (_marker)) {
2948 _editor->selection->set (_marker);
2951 case Selection::Extend:
2953 Locations::LocationList ll;
2954 list<Marker*> to_add;
2956 _editor->selection->markers.range (s, e);
2957 s = min (_marker->position(), s);
2958 e = max (_marker->position(), e);
2961 if (e < max_framepos) {
2964 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2965 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2966 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2969 to_add.push_back (lm->start);
2972 to_add.push_back (lm->end);
2976 if (!to_add.empty()) {
2977 _editor->selection->add (to_add);
2981 case Selection::Add:
2982 _editor->selection->add (_marker);
2986 /* Set up copies for us to manipulate during the drag
2989 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2991 Location* l = _editor->find_location_from_marker (*i, is_start);
2998 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3000 /* range: check that the other end of the range isn't
3003 CopiedLocationInfo::iterator x;
3004 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3005 if (*(*x).location == *l) {
3009 if (x == _copied_locations.end()) {
3010 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3012 (*x).markers.push_back (*i);
3013 (*x).move_both = true;
3021 MarkerDrag::setup_pointer_frame_offset ()
3024 Location *location = _editor->find_location_from_marker (_marker, is_start);
3025 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3029 MarkerDrag::motion (GdkEvent* event, bool)
3031 framecnt_t f_delta = 0;
3033 bool move_both = false;
3034 Location *real_location;
3035 Location *copy_location = 0;
3037 framepos_t const newframe = adjusted_current_frame (event);
3038 framepos_t next = newframe;
3040 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3044 CopiedLocationInfo::iterator x;
3046 /* find the marker we're dragging, and compute the delta */
3048 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3050 copy_location = (*x).location;
3052 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3054 /* this marker is represented by this
3055 * CopiedLocationMarkerInfo
3058 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3063 if (real_location->is_mark()) {
3064 f_delta = newframe - copy_location->start();
3068 switch (_marker->type()) {
3069 case Marker::SessionStart:
3070 case Marker::RangeStart:
3071 case Marker::LoopStart:
3072 case Marker::PunchIn:
3073 f_delta = newframe - copy_location->start();
3076 case Marker::SessionEnd:
3077 case Marker::RangeEnd:
3078 case Marker::LoopEnd:
3079 case Marker::PunchOut:
3080 f_delta = newframe - copy_location->end();
3083 /* what kind of marker is this ? */
3092 if (x == _copied_locations.end()) {
3093 /* hmm, impossible - we didn't find the dragged marker */
3097 /* now move them all */
3099 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3101 copy_location = x->location;
3103 /* call this to find out if its the start or end */
3105 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3109 if (real_location->locked()) {
3113 if (copy_location->is_mark()) {
3117 copy_location->set_start (copy_location->start() + f_delta);
3121 framepos_t new_start = copy_location->start() + f_delta;
3122 framepos_t new_end = copy_location->end() + f_delta;
3124 if (is_start) { // start-of-range marker
3126 if (move_both || (*x).move_both) {
3127 copy_location->set_start (new_start);
3128 copy_location->set_end (new_end);
3129 } else if (new_start < copy_location->end()) {
3130 copy_location->set_start (new_start);
3131 } else if (newframe > 0) {
3132 _editor->snap_to (next, 1, true);
3133 copy_location->set_end (next);
3134 copy_location->set_start (newframe);
3137 } else { // end marker
3139 if (move_both || (*x).move_both) {
3140 copy_location->set_end (new_end);
3141 copy_location->set_start (new_start);
3142 } else if (new_end > copy_location->start()) {
3143 copy_location->set_end (new_end);
3144 } else if (newframe > 0) {
3145 _editor->snap_to (next, -1, true);
3146 copy_location->set_start (next);
3147 copy_location->set_end (newframe);
3152 update_item (copy_location);
3154 /* now lookup the actual GUI items used to display this
3155 * location and move them to wherever the copy of the location
3156 * is now. This means that the logic in ARDOUR::Location is
3157 * still enforced, even though we are not (yet) modifying
3158 * the real Location itself.
3161 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3164 lm->set_position (copy_location->start(), copy_location->end());
3169 assert (!_copied_locations.empty());
3171 show_verbose_cursor_time (newframe);
3175 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3177 if (!movement_occurred) {
3179 if (was_double_click()) {
3180 _editor->rename_marker (_marker);
3184 /* just a click, do nothing but finish
3185 off the selection process
3188 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3191 case Selection::Set:
3192 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3193 _editor->selection->set (_marker);
3197 case Selection::Toggle:
3198 /* we toggle on the button release, click only */
3199 _editor->selection->toggle (_marker);
3202 case Selection::Extend:
3203 case Selection::Add:
3210 _editor->_dragging_edit_point = false;
3212 _editor->begin_reversible_command ( _("move marker") );
3213 XMLNode &before = _editor->session()->locations()->get_state();
3215 MarkerSelection::iterator i;
3216 CopiedLocationInfo::iterator x;
3219 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3220 x != _copied_locations.end() && i != _editor->selection->markers.end();
3223 Location * location = _editor->find_location_from_marker (*i, is_start);
3227 if (location->locked()) {
3231 if (location->is_mark()) {
3232 location->set_start (((*x).location)->start());
3234 location->set (((*x).location)->start(), ((*x).location)->end());
3239 XMLNode &after = _editor->session()->locations()->get_state();
3240 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3241 _editor->commit_reversible_command ();
3245 MarkerDrag::aborted (bool)
3251 MarkerDrag::update_item (Location*)
3256 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3258 _cumulative_x_drag (0),
3259 _cumulative_y_drag (0)
3261 if (_zero_gain_fraction < 0.0) {
3262 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3265 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3267 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3273 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3275 Drag::start_grab (event, _editor->cursors()->fader);
3277 // start the grab at the center of the control point so
3278 // the point doesn't 'jump' to the mouse after the first drag
3279 _fixed_grab_x = _point->get_x();
3280 _fixed_grab_y = _point->get_y();
3282 float const fraction = 1 - (_point->get_y() / _point->line().height());
3284 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3286 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3287 event->button.x + 10, event->button.y + 10);
3289 _editor->verbose_cursor()->show ();
3291 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3293 if (!_point->can_slide ()) {
3294 _x_constrained = true;
3299 ControlPointDrag::motion (GdkEvent* event, bool)
3301 double dx = _drags->current_pointer_x() - last_pointer_x();
3302 double dy = current_pointer_y() - last_pointer_y();
3304 if (event->button.state & Keyboard::SecondaryModifier) {
3309 /* coordinate in pixels relative to the start of the region (for region-based automation)
3310 or track (for track-based automation) */
3311 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3312 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3314 // calculate zero crossing point. back off by .01 to stay on the
3315 // positive side of zero
3316 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3318 // make sure we hit zero when passing through
3319 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3323 if (_x_constrained) {
3326 if (_y_constrained) {
3330 _cumulative_x_drag = cx - _fixed_grab_x;
3331 _cumulative_y_drag = cy - _fixed_grab_y;
3335 cy = min ((double) _point->line().height(), cy);
3337 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3339 if (!_x_constrained) {
3340 _editor->snap_to_with_modifier (cx_frames, event);
3343 cx_frames = min (cx_frames, _point->line().maximum_time());
3345 float const fraction = 1.0 - (cy / _point->line().height());
3347 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3349 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3353 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3355 if (!movement_occurred) {
3359 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3360 _editor->reset_point_selection ();
3364 motion (event, false);
3367 _point->line().end_drag (_pushing, _final_index);
3368 _editor->session()->commit_reversible_command ();
3372 ControlPointDrag::aborted (bool)
3374 _point->line().reset ();
3378 ControlPointDrag::active (Editing::MouseMode m)
3380 if (m == Editing::MouseGain) {
3381 /* always active in mouse gain */
3385 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3386 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3389 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3392 _cumulative_y_drag (0)
3394 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3398 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3400 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3403 _item = &_line->grab_item ();
3405 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3406 origin, and ditto for y.
3409 double cx = event->button.x;
3410 double cy = event->button.y;
3412 _line->parent_group().canvas_to_item (cx, cy);
3414 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3419 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3420 /* no adjacent points */
3424 Drag::start_grab (event, _editor->cursors()->fader);
3426 /* store grab start in parent frame */
3431 double fraction = 1.0 - (cy / _line->height());
3433 _line->start_drag_line (before, after, fraction);
3435 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3436 event->button.x + 10, event->button.y + 10);
3438 _editor->verbose_cursor()->show ();
3442 LineDrag::motion (GdkEvent* event, bool)
3444 double dy = current_pointer_y() - last_pointer_y();
3446 if (event->button.state & Keyboard::SecondaryModifier) {
3450 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3452 _cumulative_y_drag = cy - _fixed_grab_y;
3455 cy = min ((double) _line->height(), cy);
3457 double const fraction = 1.0 - (cy / _line->height());
3460 /* we are ignoring x position for this drag, so we can just pass in anything */
3461 _line->drag_motion (0, fraction, true, false, ignored);
3463 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3467 LineDrag::finished (GdkEvent* event, bool movement_occured)
3469 if (movement_occured) {
3470 motion (event, false);
3471 _line->end_drag (false, 0);
3473 /* add a new control point on the line */
3475 AutomationTimeAxisView* atv;
3477 _line->end_drag (false, 0);
3479 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3480 framepos_t where = _editor->window_event_sample (event, 0, 0);
3481 atv->add_automation_event (event, where, event->button.y, false);
3485 _editor->session()->commit_reversible_command ();
3489 LineDrag::aborted (bool)
3494 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3497 _cumulative_x_drag (0)
3499 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3503 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3505 Drag::start_grab (event);
3507 _line = reinterpret_cast<Line*> (_item);
3510 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3512 double cx = event->button.x;
3513 double cy = event->button.y;
3515 _item->parent()->canvas_to_item (cx, cy);
3517 /* store grab start in parent frame */
3518 _region_view_grab_x = cx;
3520 _before = *(float*) _item->get_data ("position");
3522 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3524 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3528 FeatureLineDrag::motion (GdkEvent*, bool)
3530 double dx = _drags->current_pointer_x() - last_pointer_x();
3532 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3534 _cumulative_x_drag += dx;
3536 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3545 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3547 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3549 float *pos = new float;
3552 _line->set_data ("position", pos);
3558 FeatureLineDrag::finished (GdkEvent*, bool)
3560 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3561 _arv->update_transient(_before, _before);
3565 FeatureLineDrag::aborted (bool)
3570 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3572 , _vertical_only (false)
3574 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3578 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3580 Drag::start_grab (event);
3581 show_verbose_cursor_time (adjusted_current_frame (event));
3585 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3592 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3594 framepos_t grab = grab_frame ();
3595 if (Config->get_rubberbanding_snaps_to_grid ()) {
3596 _editor->snap_to_with_modifier (grab, event);
3599 /* base start and end on initial click position */
3609 if (current_pointer_y() < grab_y()) {
3610 y1 = current_pointer_y();
3613 y2 = current_pointer_y();
3617 if (start != end || y1 != y2) {
3619 double x1 = _editor->sample_to_pixel (start);
3620 double x2 = _editor->sample_to_pixel (end);
3621 const double min_dimension = 2.0;
3623 if (_vertical_only) {
3624 /* fixed 10 pixel width */
3628 x2 = min (x1 - min_dimension, x2);
3630 x2 = max (x1 + min_dimension, x2);
3635 y2 = min (y1 - min_dimension, y2);
3637 y2 = max (y1 + min_dimension, y2);
3640 /* translate rect into item space and set */
3642 ArdourCanvas::Rect r (x1, y1, x2, y2);
3644 /* this drag is a _trackview_only == true drag, so the y1 and
3645 * y2 (computed using current_pointer_y() and grab_y()) will be
3646 * relative to the top of the trackview group). The
3647 * rubberband rect has the same parent/scroll offset as the
3648 * the trackview group, so we can use the "r" rect directly
3649 * to set the shape of the rubberband.
3652 _editor->rubberband_rect->set (r);
3653 _editor->rubberband_rect->show();
3654 _editor->rubberband_rect->raise_to_top();
3656 show_verbose_cursor_time (pf);
3658 do_select_things (event, true);
3663 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3668 if (grab_frame() < last_pointer_frame()) {
3670 x2 = last_pointer_frame ();
3673 x1 = last_pointer_frame ();
3679 if (current_pointer_y() < grab_y()) {
3680 y1 = current_pointer_y();
3683 y2 = current_pointer_y();
3687 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3691 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3693 if (movement_occurred) {
3695 motion (event, false);
3696 do_select_things (event, false);
3702 bool do_deselect = true;
3703 MidiTimeAxisView* mtv;
3705 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3707 if (_editor->selection->empty()) {
3708 /* nothing selected */
3709 add_midi_region (mtv);
3710 do_deselect = false;
3714 /* do not deselect if Primary or Tertiary (toggle-select or
3715 * extend-select are pressed.
3718 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3719 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3726 _editor->rubberband_rect->hide();
3730 RubberbandSelectDrag::aborted (bool)
3732 _editor->rubberband_rect->hide ();
3735 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3736 : RegionDrag (e, i, p, v)
3738 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3742 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3744 Drag::start_grab (event, cursor);
3746 show_verbose_cursor_time (adjusted_current_frame (event));
3750 TimeFXDrag::motion (GdkEvent* event, bool)
3752 RegionView* rv = _primary;
3753 StreamView* cv = rv->get_time_axis_view().view ();
3755 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3756 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3757 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3759 framepos_t const pf = adjusted_current_frame (event);
3761 if (pf > rv->region()->position()) {
3762 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3765 show_verbose_cursor_time (pf);
3769 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3771 _primary->get_time_axis_view().hide_timestretch ();
3773 if (!movement_occurred) {
3777 if (last_pointer_frame() < _primary->region()->position()) {
3778 /* backwards drag of the left edge - not usable */
3782 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3784 float percentage = (double) newlen / (double) _primary->region()->length();
3786 #ifndef USE_RUBBERBAND
3787 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3788 if (_primary->region()->data_type() == DataType::AUDIO) {
3789 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3793 if (!_editor->get_selection().regions.empty()) {
3794 /* primary will already be included in the selection, and edit
3795 group shared editing will propagate selection across
3796 equivalent regions, so just use the current region
3800 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3801 error << _("An error occurred while executing time stretch operation") << endmsg;
3807 TimeFXDrag::aborted (bool)
3809 _primary->get_time_axis_view().hide_timestretch ();
3812 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3815 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3819 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3821 Drag::start_grab (event);
3825 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3827 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3831 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3833 if (movement_occurred && _editor->session()) {
3834 /* make sure we stop */
3835 _editor->session()->request_transport_speed (0.0);
3840 ScrubDrag::aborted (bool)
3845 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3850 , _original_pointer_time_axis (-1)
3851 , _last_pointer_time_axis (-1)
3852 , _time_selection_at_start (!_editor->get_selection().time.empty())
3854 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3856 if (_time_selection_at_start) {
3857 start_at_start = _editor->get_selection().time.start();
3858 end_at_start = _editor->get_selection().time.end_frame();
3863 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3865 if (_editor->session() == 0) {
3869 Gdk::Cursor* cursor = 0;
3871 switch (_operation) {
3872 case CreateSelection:
3873 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3878 cursor = _editor->cursors()->selector;
3879 Drag::start_grab (event, cursor);
3882 case SelectionStartTrim:
3883 if (_editor->clicked_axisview) {
3884 _editor->clicked_axisview->order_selection_trims (_item, true);
3886 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3889 case SelectionEndTrim:
3890 if (_editor->clicked_axisview) {
3891 _editor->clicked_axisview->order_selection_trims (_item, false);
3893 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3897 Drag::start_grab (event, cursor);
3900 case SelectionExtend:
3901 Drag::start_grab (event, cursor);
3905 if (_operation == SelectionMove) {
3906 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3908 show_verbose_cursor_time (adjusted_current_frame (event));
3911 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
3915 SelectionDrag::setup_pointer_frame_offset ()
3917 switch (_operation) {
3918 case CreateSelection:
3919 _pointer_frame_offset = 0;
3922 case SelectionStartTrim:
3924 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3927 case SelectionEndTrim:
3928 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3931 case SelectionExtend:
3937 SelectionDrag::motion (GdkEvent* event, bool first_move)
3939 framepos_t start = 0;
3941 framecnt_t length = 0;
3942 framecnt_t distance = 0;
3944 framepos_t const pending_position = adjusted_current_frame (event);
3946 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3950 switch (_operation) {
3951 case CreateSelection:
3953 framepos_t grab = grab_frame ();
3956 grab = adjusted_current_frame (event, false);
3957 if (grab < pending_position) {
3958 _editor->snap_to (grab, -1);
3960 _editor->snap_to (grab, 1);
3964 if (pending_position < grab) {
3965 start = pending_position;
3968 end = pending_position;
3972 /* first drag: Either add to the selection
3973 or create a new selection
3980 /* adding to the selection */
3981 _editor->set_selected_track_as_side_effect (Selection::Add);
3982 _editor->clicked_selection = _editor->selection->add (start, end);
3989 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3990 _editor->set_selected_track_as_side_effect (Selection::Set);
3993 _editor->clicked_selection = _editor->selection->set (start, end);
3997 /* select all tracks within the rectangle that we've marked out so far */
3998 TrackViewList to_be_added_to_selection;
3999 TrackViewList to_be_removed_from_selection;
4000 TrackViewList& all_tracks (_editor->track_views);
4002 ArdourCanvas::Coord const top = grab_y();
4003 ArdourCanvas::Coord const bottom = current_pointer_y();
4005 if (top >= 0 && bottom >= 0) {
4007 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4009 if ((*i)->covered_by_y_range (top, bottom)) {
4010 if (!(*i)->get_selected()) {
4011 to_be_added_to_selection.push_back (*i);
4014 if ((*i)->get_selected()) {
4015 to_be_removed_from_selection.push_back (*i);
4020 if (!to_be_added_to_selection.empty()) {
4021 _editor->selection->add (to_be_added_to_selection);
4024 if (!to_be_removed_from_selection.empty()) {
4025 _editor->selection->remove (to_be_removed_from_selection);
4031 case SelectionStartTrim:
4033 start = _editor->selection->time[_editor->clicked_selection].start;
4034 end = _editor->selection->time[_editor->clicked_selection].end;
4036 if (pending_position > end) {
4039 start = pending_position;
4043 case SelectionEndTrim:
4045 start = _editor->selection->time[_editor->clicked_selection].start;
4046 end = _editor->selection->time[_editor->clicked_selection].end;
4048 if (pending_position < start) {
4051 end = pending_position;
4058 start = _editor->selection->time[_editor->clicked_selection].start;
4059 end = _editor->selection->time[_editor->clicked_selection].end;
4061 length = end - start;
4062 distance = pending_position - start;
4063 start = pending_position;
4064 _editor->snap_to (start);
4066 end = start + length;
4070 case SelectionExtend:
4075 switch (_operation) {
4077 if (_time_selection_at_start) {
4078 _editor->selection->move_time (distance);
4082 _editor->selection->replace (_editor->clicked_selection, start, end);
4086 if (_operation == SelectionMove) {
4087 show_verbose_cursor_time(start);
4089 show_verbose_cursor_time(pending_position);
4094 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4096 Session* s = _editor->session();
4098 if (movement_occurred) {
4099 motion (event, false);
4100 /* XXX this is not object-oriented programming at all. ick */
4101 if (_editor->selection->time.consolidate()) {
4102 _editor->selection->TimeChanged ();
4105 /* XXX what if its a music time selection? */
4107 if ( s->get_play_range() && s->transport_rolling() ) {
4108 s->request_play_range (&_editor->selection->time, true);
4110 if (Config->get_always_play_range() && !s->transport_rolling()) {
4111 s->request_locate (_editor->get_selection().time.start());
4117 /* just a click, no pointer movement.
4120 if (_operation == SelectionExtend) {
4121 if (_time_selection_at_start) {
4122 framepos_t pos = adjusted_current_frame (event, false);
4123 framepos_t start = min (pos, start_at_start);
4124 framepos_t end = max (pos, end_at_start);
4125 _editor->selection->set (start, end);
4128 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4129 if (_editor->clicked_selection) {
4130 _editor->selection->remove (_editor->clicked_selection);
4133 if (!_editor->clicked_selection) {
4134 _editor->selection->clear_time();
4139 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4140 _editor->selection->set (_editor->clicked_axisview);
4143 if (s && s->get_play_range () && s->transport_rolling()) {
4144 s->request_stop (false, false);
4149 _editor->stop_canvas_autoscroll ();
4150 _editor->clicked_selection = 0;
4154 SelectionDrag::aborted (bool)
4159 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4160 : Drag (e, i, false),
4164 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4166 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4167 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4168 physical_screen_height (_editor->get_window())));
4169 _drag_rect->hide ();
4171 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4172 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4176 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4178 if (_editor->session() == 0) {
4182 Gdk::Cursor* cursor = 0;
4184 if (!_editor->temp_location) {
4185 _editor->temp_location = new Location (*_editor->session());
4188 switch (_operation) {
4189 case CreateRangeMarker:
4190 case CreateTransportMarker:
4191 case CreateCDMarker:
4193 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4198 cursor = _editor->cursors()->selector;
4202 Drag::start_grab (event, cursor);
4204 show_verbose_cursor_time (adjusted_current_frame (event));
4208 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4210 framepos_t start = 0;
4212 ArdourCanvas::Rectangle *crect;
4214 switch (_operation) {
4215 case CreateRangeMarker:
4216 crect = _editor->range_bar_drag_rect;
4218 case CreateTransportMarker:
4219 crect = _editor->transport_bar_drag_rect;
4221 case CreateCDMarker:
4222 crect = _editor->cd_marker_bar_drag_rect;
4225 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4230 framepos_t const pf = adjusted_current_frame (event);
4232 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4233 framepos_t grab = grab_frame ();
4234 _editor->snap_to (grab);
4236 if (pf < grab_frame()) {
4244 /* first drag: Either add to the selection
4245 or create a new selection.
4250 _editor->temp_location->set (start, end);
4254 update_item (_editor->temp_location);
4256 //_drag_rect->raise_to_top();
4262 _editor->temp_location->set (start, end);
4264 double x1 = _editor->sample_to_pixel (start);
4265 double x2 = _editor->sample_to_pixel (end);
4269 update_item (_editor->temp_location);
4272 show_verbose_cursor_time (pf);
4277 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4279 Location * newloc = 0;
4283 if (movement_occurred) {
4284 motion (event, false);
4287 switch (_operation) {
4288 case CreateRangeMarker:
4289 case CreateCDMarker:
4291 _editor->begin_reversible_command (_("new range marker"));
4292 XMLNode &before = _editor->session()->locations()->get_state();
4293 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4294 if (_operation == CreateCDMarker) {
4295 flags = Location::IsRangeMarker | Location::IsCDMarker;
4296 _editor->cd_marker_bar_drag_rect->hide();
4299 flags = Location::IsRangeMarker;
4300 _editor->range_bar_drag_rect->hide();
4302 newloc = new Location (
4303 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4306 _editor->session()->locations()->add (newloc, true);
4307 XMLNode &after = _editor->session()->locations()->get_state();
4308 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4309 _editor->commit_reversible_command ();
4313 case CreateTransportMarker:
4314 // popup menu to pick loop or punch
4315 _editor->new_transport_marker_context_menu (&event->button, _item);
4321 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4323 if (_operation == CreateTransportMarker) {
4325 /* didn't drag, so just locate */
4327 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4329 } else if (_operation == CreateCDMarker) {
4331 /* didn't drag, but mark is already created so do
4334 } else { /* operation == CreateRangeMarker */
4340 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4342 if (end == max_framepos) {
4343 end = _editor->session()->current_end_frame ();
4346 if (start == max_framepos) {
4347 start = _editor->session()->current_start_frame ();
4350 switch (_editor->mouse_mode) {
4352 /* find the two markers on either side and then make the selection from it */
4353 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4357 /* find the two markers on either side of the click and make the range out of it */
4358 _editor->selection->set (start, end);
4367 _editor->stop_canvas_autoscroll ();
4371 RangeMarkerBarDrag::aborted (bool)
4377 RangeMarkerBarDrag::update_item (Location* location)
4379 double const x1 = _editor->sample_to_pixel (location->start());
4380 double const x2 = _editor->sample_to_pixel (location->end());
4382 _drag_rect->set_x0 (x1);
4383 _drag_rect->set_x1 (x2);
4386 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4390 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4394 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4396 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4397 Drag::start_grab (event, _editor->cursors()->zoom_out);
4400 Drag::start_grab (event, _editor->cursors()->zoom_in);
4404 show_verbose_cursor_time (adjusted_current_frame (event));
4408 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4413 framepos_t const pf = adjusted_current_frame (event);
4415 framepos_t grab = grab_frame ();
4416 _editor->snap_to_with_modifier (grab, event);
4418 /* base start and end on initial click position */
4430 _editor->zoom_rect->show();
4431 _editor->zoom_rect->raise_to_top();
4434 _editor->reposition_zoom_rect(start, end);
4436 show_verbose_cursor_time (pf);
4441 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4443 if (movement_occurred) {
4444 motion (event, false);
4446 if (grab_frame() < last_pointer_frame()) {
4447 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4449 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4452 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4453 _editor->tav_zoom_step (_zoom_out);
4455 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4459 _editor->zoom_rect->hide();
4463 MouseZoomDrag::aborted (bool)
4465 _editor->zoom_rect->hide ();
4468 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4470 , _cumulative_dx (0)
4471 , _cumulative_dy (0)
4473 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4475 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4477 _region = &_primary->region_view ();
4478 _note_height = _region->midi_stream_view()->note_height ();
4482 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4484 Drag::start_grab (event);
4486 if (!(_was_selected = _primary->selected())) {
4488 /* tertiary-click means extend selection - we'll do that on button release,
4489 so don't add it here, because otherwise we make it hard to figure
4490 out the "extend-to" range.
4493 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4496 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4499 _region->note_selected (_primary, true);
4501 _region->unique_select (_primary);
4507 /** @return Current total drag x change in frames */
4509 NoteDrag::total_dx () const
4512 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4514 /* primary note time */
4515 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4517 /* new time of the primary note in session frames */
4518 frameoffset_t st = n + dx;
4520 framepos_t const rp = _region->region()->position ();
4522 /* prevent the note being dragged earlier than the region's position */
4525 /* snap and return corresponding delta */
4526 return _region->snap_frame_to_frame (st - rp) + rp - n;
4529 /** @return Current total drag y change in note number */
4531 NoteDrag::total_dy () const
4533 MidiStreamView* msv = _region->midi_stream_view ();
4534 double const y = _region->midi_view()->y_position ();
4535 /* new current note */
4536 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4538 n = max (msv->lowest_note(), n);
4539 n = min (msv->highest_note(), n);
4540 /* and work out delta */
4541 return n - msv->y_to_note (grab_y() - y);
4545 NoteDrag::motion (GdkEvent *, bool)
4547 /* Total change in x and y since the start of the drag */
4548 frameoffset_t const dx = total_dx ();
4549 int8_t const dy = total_dy ();
4551 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4552 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4553 double const tdy = -dy * _note_height - _cumulative_dy;
4556 _cumulative_dx += tdx;
4557 _cumulative_dy += tdy;
4559 int8_t note_delta = total_dy();
4561 _region->move_selection (tdx, tdy, note_delta);
4563 /* the new note value may be the same as the old one, but we
4564 * don't know what that means because the selection may have
4565 * involved more than one note and we might be doing something
4566 * odd with them. so show the note value anyway, always.
4570 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4572 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4573 (int) floor ((double)new_note));
4575 show_verbose_cursor_text (buf);
4580 NoteDrag::finished (GdkEvent* ev, bool moved)
4583 /* no motion - select note */
4585 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4586 _editor->current_mouse_mode() == Editing::MouseDraw) {
4588 if (_was_selected) {
4589 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4591 _region->note_deselected (_primary);
4594 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4595 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4597 if (!extend && !add && _region->selection_size() > 1) {
4598 _region->unique_select (_primary);
4599 } else if (extend) {
4600 _region->note_selected (_primary, true, true);
4602 /* it was added during button press */
4607 _region->note_dropped (_primary, total_dx(), total_dy());
4612 NoteDrag::aborted (bool)
4617 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4618 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4619 : Drag (editor, atv->base_item ())
4621 , _nothing_to_drag (false)
4623 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4624 y_origin = atv->y_position();
4625 setup (atv->lines ());
4628 /** Make an AutomationRangeDrag for region gain lines */
4629 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4630 : Drag (editor, rv->get_canvas_group ())
4632 , _nothing_to_drag (false)
4634 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4636 list<boost::shared_ptr<AutomationLine> > lines;
4637 lines.push_back (rv->get_gain_line ());
4638 y_origin = rv->get_time_axis_view().y_position();
4642 /** @param lines AutomationLines to drag.
4643 * @param offset Offset from the session start to the points in the AutomationLines.
4646 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4648 /* find the lines that overlap the ranges being dragged */
4649 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4650 while (i != lines.end ()) {
4651 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4654 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4656 /* check this range against all the AudioRanges that we are using */
4657 list<AudioRange>::const_iterator k = _ranges.begin ();
4658 while (k != _ranges.end()) {
4659 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4665 /* add it to our list if it overlaps at all */
4666 if (k != _ranges.end()) {
4671 _lines.push_back (n);
4677 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4681 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4683 return 1.0 - ((global_y - y_origin) / line->height());
4687 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4689 Drag::start_grab (event, cursor);
4691 /* Get line states before we start changing things */
4692 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4693 i->state = &i->line->get_state ();
4694 i->original_fraction = y_fraction (i->line, current_pointer_y());
4697 if (_ranges.empty()) {
4699 /* No selected time ranges: drag all points */
4700 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4701 uint32_t const N = i->line->npoints ();
4702 for (uint32_t j = 0; j < N; ++j) {
4703 i->points.push_back (i->line->nth (j));
4709 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4711 framecnt_t const half = (i->start + i->end) / 2;
4713 /* find the line that this audio range starts in */
4714 list<Line>::iterator j = _lines.begin();
4715 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4719 if (j != _lines.end()) {
4720 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4722 /* j is the line that this audio range starts in; fade into it;
4723 64 samples length plucked out of thin air.
4726 framepos_t a = i->start + 64;
4731 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4732 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4734 the_list->add (p, the_list->eval (p));
4735 the_list->add (q, the_list->eval (q));
4738 /* same thing for the end */
4741 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4745 if (j != _lines.end()) {
4746 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4748 /* j is the line that this audio range starts in; fade out of it;
4749 64 samples length plucked out of thin air.
4752 framepos_t b = i->end - 64;
4757 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4758 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4760 the_list->add (p, the_list->eval (p));
4761 the_list->add (q, the_list->eval (q));
4765 _nothing_to_drag = true;
4767 /* Find all the points that should be dragged and put them in the relevant
4768 points lists in the Line structs.
4771 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4773 uint32_t const N = i->line->npoints ();
4774 for (uint32_t j = 0; j < N; ++j) {
4776 /* here's a control point on this line */
4777 ControlPoint* p = i->line->nth (j);
4778 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4780 /* see if it's inside a range */
4781 list<AudioRange>::const_iterator k = _ranges.begin ();
4782 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4786 if (k != _ranges.end()) {
4787 /* dragging this point */
4788 _nothing_to_drag = false;
4789 i->points.push_back (p);
4795 if (_nothing_to_drag) {
4799 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4800 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
4805 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4807 if (_nothing_to_drag) {
4811 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4812 float const f = y_fraction (l->line, current_pointer_y());
4813 /* we are ignoring x position for this drag, so we can just pass in anything */
4815 l->line->drag_motion (0, f, true, false, ignored);
4816 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4821 AutomationRangeDrag::finished (GdkEvent* event, bool)
4823 if (_nothing_to_drag) {
4827 motion (event, false);
4828 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4829 i->line->end_drag (false, 0);
4832 _editor->session()->commit_reversible_command ();
4836 AutomationRangeDrag::aborted (bool)
4838 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4843 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4846 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4847 layer = v->region()->layer ();
4848 initial_y = v->get_canvas_group()->position().y;
4849 initial_playlist = v->region()->playlist ();
4850 initial_position = v->region()->position ();
4851 initial_end = v->region()->position () + v->region()->length ();
4854 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4855 : Drag (e, i->canvas_item ())
4858 , _cumulative_dx (0)
4860 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4861 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4866 PatchChangeDrag::motion (GdkEvent* ev, bool)
4868 framepos_t f = adjusted_current_frame (ev);
4869 boost::shared_ptr<Region> r = _region_view->region ();
4870 f = max (f, r->position ());
4871 f = min (f, r->last_frame ());
4873 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4874 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4875 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4876 _cumulative_dx = dxu;
4880 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4882 if (!movement_occurred) {
4886 boost::shared_ptr<Region> r (_region_view->region ());
4887 framepos_t f = adjusted_current_frame (ev);
4888 f = max (f, r->position ());
4889 f = min (f, r->last_frame ());
4891 _region_view->move_patch_change (
4893 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4898 PatchChangeDrag::aborted (bool)
4900 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4904 PatchChangeDrag::setup_pointer_frame_offset ()
4906 boost::shared_ptr<Region> region = _region_view->region ();
4907 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4910 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4911 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4918 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4920 framepos_t const p = _region_view->region()->position ();
4921 double const y = _region_view->midi_view()->y_position ();
4923 x1 = max ((framepos_t) 0, x1 - p);
4924 x2 = max ((framepos_t) 0, x2 - p);
4925 y1 = max (0.0, y1 - y);
4926 y2 = max (0.0, y2 - y);
4928 _region_view->update_drag_selection (
4929 _editor->sample_to_pixel (x1),
4930 _editor->sample_to_pixel (x2),
4933 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4938 MidiRubberbandSelectDrag::deselect_things ()
4943 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4944 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4947 _vertical_only = true;
4951 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4953 double const y = _region_view->midi_view()->y_position ();
4955 y1 = max (0.0, y1 - y);
4956 y2 = max (0.0, y2 - y);
4958 _region_view->update_vertical_drag_selection (
4961 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4966 MidiVerticalSelectDrag::deselect_things ()
4971 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4972 : RubberbandSelectDrag (e, i)
4978 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4980 if (drag_in_progress) {
4981 /* We just want to select things at the end of the drag, not during it */
4985 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4987 _editor->begin_reversible_command (_("rubberband selection"));
4988 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4989 _editor->commit_reversible_command ();
4993 EditorRubberbandSelectDrag::deselect_things ()
4995 if (!getenv("ARDOUR_SAE")) {
4996 _editor->selection->clear_tracks();
4998 _editor->selection->clear_regions();
4999 _editor->selection->clear_points ();
5000 _editor->selection->clear_lines ();
5003 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5011 NoteCreateDrag::~NoteCreateDrag ()
5017 NoteCreateDrag::grid_frames (framepos_t t) const
5020 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5025 return _region_view->region_beats_to_region_frames (grid_beats);
5029 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5031 Drag::start_grab (event, cursor);
5033 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5035 framepos_t pf = _drags->current_pointer_frame ();
5036 framecnt_t const g = grid_frames (pf);
5038 /* Hack so that we always snap to the note that we are over, instead of snapping
5039 to the next one if we're more than halfway through the one we're over.
5041 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5045 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5047 MidiStreamView* sv = _region_view->midi_stream_view ();
5048 double const x = _editor->sample_to_pixel (_note[0]);
5049 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5051 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5052 _drag_rect->set_outline_all ();
5053 _drag_rect->set_outline_color (0xffffff99);
5054 _drag_rect->set_fill_color (0xffffff66);
5058 NoteCreateDrag::motion (GdkEvent* event, bool)
5060 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5061 double const x = _editor->sample_to_pixel (_note[1]);
5062 if (_note[1] > _note[0]) {
5063 _drag_rect->set_x1 (x);
5065 _drag_rect->set_x0 (x);
5070 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5072 if (!had_movement) {
5076 framepos_t const start = min (_note[0], _note[1]);
5077 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5079 framecnt_t const g = grid_frames (start);
5080 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5082 if (_editor->snap_mode() == SnapNormal && length < g) {
5083 length = g - one_tick;
5086 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5088 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5092 NoteCreateDrag::y_to_region (double y) const
5095 _region_view->get_canvas_group()->canvas_to_item (x, y);
5100 NoteCreateDrag::aborted (bool)
5105 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5110 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5114 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5116 Drag::start_grab (event, cursor);
5120 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5126 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5129 distance = _drags->current_pointer_x() - grab_x();
5130 len = ar->fade_in()->back()->when;
5132 distance = grab_x() - _drags->current_pointer_x();
5133 len = ar->fade_out()->back()->when;
5136 /* how long should it be ? */
5138 new_length = len + _editor->pixel_to_sample (distance);
5140 /* now check with the region that this is legal */
5142 new_length = ar->verify_xfade_bounds (new_length, start);
5145 arv->reset_fade_in_shape_width (ar, new_length);
5147 arv->reset_fade_out_shape_width (ar, new_length);
5152 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5158 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5161 distance = _drags->current_pointer_x() - grab_x();
5162 len = ar->fade_in()->back()->when;
5164 distance = grab_x() - _drags->current_pointer_x();
5165 len = ar->fade_out()->back()->when;
5168 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5170 _editor->begin_reversible_command ("xfade trim");
5171 ar->playlist()->clear_owned_changes ();
5174 ar->set_fade_in_length (new_length);
5176 ar->set_fade_out_length (new_length);
5179 /* Adjusting the xfade may affect other regions in the playlist, so we need
5180 to get undo Commands from the whole playlist rather than just the
5184 vector<Command*> cmds;
5185 ar->playlist()->rdiff (cmds);
5186 _editor->session()->add_commands (cmds);
5187 _editor->commit_reversible_command ();
5192 CrossfadeEdgeDrag::aborted (bool)
5195 arv->redraw_start_xfade ();
5197 arv->redraw_end_xfade ();