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 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
182 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
183 bool const t = (*i)->motion_handler (e, from_autoscroll);
194 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
198 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
200 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
201 bool const t = (*i)->motion_handler (e, from_autoscroll);
212 DragManager::have_item (ArdourCanvas::Item* i) const
214 list<Drag*>::const_iterator j = _drags.begin ();
215 while (j != _drags.end() && (*j)->item () != i) {
219 return j != _drags.end ();
222 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
225 , _pointer_frame_offset (0)
226 , _move_threshold_passed (false)
227 , _was_double_click (false)
228 , _raw_grab_frame (0)
230 , _last_pointer_frame (0)
236 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
249 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
251 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
253 if (Keyboard::is_button2_event (&event->button)) {
254 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
255 _y_constrained = true;
256 _x_constrained = false;
258 _y_constrained = false;
259 _x_constrained = true;
262 _x_constrained = false;
263 _y_constrained = false;
266 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
271 _last_pointer_y = _grab_y;
277 /* CAIROCANVAS need a variant here that passes *cursor */
282 if (_editor->session() && _editor->session()->transport_rolling()) {
285 _was_rolling = false;
288 switch (_editor->snap_type()) {
289 case SnapToRegionStart:
290 case SnapToRegionEnd:
291 case SnapToRegionSync:
292 case SnapToRegionBoundary:
293 _editor->build_region_boundary_cache ();
300 /** Call to end a drag `successfully'. Ungrabs item and calls
301 * subclass' finished() method.
303 * @param event GDK event, or 0.
304 * @return true if some movement occurred, otherwise false.
307 Drag::end_grab (GdkEvent* event)
309 _editor->stop_canvas_autoscroll ();
313 finished (event, _move_threshold_passed);
315 _editor->verbose_cursor()->hide ();
317 return _move_threshold_passed;
321 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
325 if (f > _pointer_frame_offset) {
326 pos = f - _pointer_frame_offset;
330 _editor->snap_to_with_modifier (pos, event);
337 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
339 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
343 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
345 /* check to see if we have moved in any way that matters since the last motion event */
346 if (_move_threshold_passed &&
347 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
348 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
352 pair<framecnt_t, int> const threshold = move_threshold ();
354 bool const old_move_threshold_passed = _move_threshold_passed;
356 if (!from_autoscroll && !_move_threshold_passed) {
358 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
359 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
361 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
364 if (active (_editor->mouse_mode) && _move_threshold_passed) {
366 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
367 if (!from_autoscroll) {
368 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
371 if (!_editor->autoscroll_active() || from_autoscroll) {
372 motion (event, _move_threshold_passed != old_move_threshold_passed);
374 _last_pointer_x = _drags->current_pointer_x ();
375 _last_pointer_y = _drags->current_pointer_y ();
376 _last_pointer_frame = adjusted_current_frame (event);
385 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
393 aborted (_move_threshold_passed);
395 _editor->stop_canvas_autoscroll ();
396 _editor->verbose_cursor()->hide ();
400 Drag::show_verbose_cursor_time (framepos_t frame)
402 _editor->verbose_cursor()->set_time (
404 _drags->current_pointer_x() + 10,
405 _drags->current_pointer_y() + 10
408 _editor->verbose_cursor()->show ();
412 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
414 _editor->verbose_cursor()->show (xoffset);
416 _editor->verbose_cursor()->set_duration (
418 _drags->current_pointer_x() + 10,
419 _drags->current_pointer_y() + 10
424 Drag::show_verbose_cursor_text (string const & text)
426 _editor->verbose_cursor()->show ();
428 _editor->verbose_cursor()->set (
430 _drags->current_pointer_x() + 10,
431 _drags->current_pointer_y() + 10
435 boost::shared_ptr<Region>
436 Drag::add_midi_region (MidiTimeAxisView* view)
438 if (_editor->session()) {
439 const TempoMap& map (_editor->session()->tempo_map());
440 framecnt_t pos = grab_frame();
441 const Meter& m = map.meter_at (pos);
442 /* not that the frame rate used here can be affected by pull up/down which
445 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
446 return view->add_region (grab_frame(), len, true);
449 return boost::shared_ptr<Region>();
452 struct EditorOrderTimeAxisViewSorter {
453 bool operator() (TimeAxisView* a, TimeAxisView* b) {
454 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
455 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
457 return ra->route()->order_key () < rb->route()->order_key ();
461 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
465 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
467 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
468 as some of the regions we are dragging may be on such tracks.
471 TrackViewList track_views = _editor->track_views;
472 track_views.sort (EditorOrderTimeAxisViewSorter ());
474 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
475 _time_axis_views.push_back (*i);
477 TimeAxisView::Children children_list = (*i)->get_child_list ();
478 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
479 _time_axis_views.push_back (j->get());
483 /* the list of views can be empty at this point if this is a region list-insert drag
486 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
487 _views.push_back (DraggingView (*i, this));
490 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
494 RegionDrag::region_going_away (RegionView* v)
496 list<DraggingView>::iterator i = _views.begin ();
497 while (i != _views.end() && i->view != v) {
501 if (i != _views.end()) {
506 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
507 * or -1 if it is not found.
510 RegionDrag::find_time_axis_view (TimeAxisView* t) const
513 int const N = _time_axis_views.size ();
514 while (i < N && _time_axis_views[i] != t) {
525 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
526 : RegionDrag (e, i, p, v)
529 , _last_pointer_time_axis_view (0)
530 , _last_pointer_layer (0)
532 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
536 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
538 Drag::start_grab (event, cursor);
540 show_verbose_cursor_time (_last_frame_position);
542 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
544 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
545 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
550 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
552 /* compute the amount of pointer motion in frames, and where
553 the region would be if we moved it by that much.
555 *pending_region_position = adjusted_current_frame (event);
557 framepos_t sync_frame;
558 framecnt_t sync_offset;
561 sync_offset = _primary->region()->sync_offset (sync_dir);
563 /* we don't handle a sync point that lies before zero.
565 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
567 sync_frame = *pending_region_position + (sync_dir*sync_offset);
569 _editor->snap_to_with_modifier (sync_frame, event);
571 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
574 *pending_region_position = _last_frame_position;
577 if (*pending_region_position > max_framepos - _primary->region()->length()) {
578 *pending_region_position = _last_frame_position;
583 /* in locked edit mode, reverse the usual meaning of _x_constrained */
584 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
586 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
588 /* x movement since last time (in pixels) */
589 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
591 /* total x movement */
592 framecnt_t total_dx = *pending_region_position;
593 if (regions_came_from_canvas()) {
594 total_dx = total_dx - grab_frame ();
597 /* check that no regions have gone off the start of the session */
598 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
599 if ((i->view->region()->position() + total_dx) < 0) {
601 *pending_region_position = _last_frame_position;
606 _last_frame_position = *pending_region_position;
613 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
615 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
616 int const n = i->time_axis_view + delta_track;
617 if (n < 0 || n >= int (_time_axis_views.size())) {
618 /* off the top or bottom track */
622 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
623 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
624 /* not a track, or the wrong type */
628 double const l = i->layer + delta_layer;
630 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
631 mode to allow the user to place a region below another on layer 0.
633 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
634 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
635 If it has, the layers will be munged later anyway, so it's ok.
641 /* all regions being dragged are ok with this change */
646 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
648 double delta_layer = 0;
649 int delta_time_axis_view = 0;
651 assert (!_views.empty ());
653 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
655 /* Find the TimeAxisView that the pointer is now over */
656 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (_drags->current_pointer_y ());
657 TimeAxisView* tv = r.first;
659 if (tv && tv->view()) {
660 double layer = r.second;
662 if (first_move && tv->view()->layer_display() == Stacked) {
663 tv->view()->set_layer_display (Expanded);
666 /* Here's the current pointer position in terms of time axis view and layer */
667 int const current_pointer_time_axis_view = find_time_axis_view (tv);
668 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
670 /* Work out the change in y */
672 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
673 delta_layer = current_pointer_layer - _last_pointer_layer;
676 /* Work out the change in x */
677 framepos_t pending_region_position;
678 double const x_delta = compute_x_delta (event, &pending_region_position);
680 /* Verify change in y */
681 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
682 /* this y movement is not allowed, so do no y movement this time */
683 delta_time_axis_view = 0;
687 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
688 /* haven't reached next snap point, and we're not switching
689 trackviews nor layers. nothing to do.
694 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
696 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
698 RegionView* rv = i->view;
700 if (rv->region()->locked() || rv->region()->video_locked()) {
706 rv->fake_set_opaque (true);
708 /* reparent the regionview into a group above all
712 ArdourCanvas::Group* rvg = rv->get_canvas_group();
713 Duple rv_canvas_offset = rvg->parent()->item_to_canvas (Duple (0,0));
714 Duple dmg_canvas_offset = _editor->_drag_motion_group->item_to_canvas (Duple (0,0));
715 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
716 /* move the item so that it continues to appear at the
717 same location now that its parent has changed.
719 rvg->move (rv_canvas_offset - dmg_canvas_offset);
722 /* If we have moved tracks, we'll fudge the layer delta so that the
723 region gets moved back onto layer 0 on its new track; this avoids
724 confusion when dragging regions from non-zero layers onto different
727 double this_delta_layer = delta_layer;
728 if (delta_time_axis_view != 0) {
729 this_delta_layer = - i->layer;
734 int track_index = i->time_axis_view + delta_time_axis_view;
736 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
740 /* The TimeAxisView that this region is now over */
741 TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
743 /* Ensure it is moved from stacked -> expanded if appropriate */
744 if (current_tv->view()->layer_display() == Stacked) {
745 current_tv->view()->set_layer_display (Expanded);
748 /* We're only allowed to go -ve in layer on Expanded views */
749 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
750 this_delta_layer = - i->layer;
754 rv->set_height (current_tv->view()->child_height ());
756 /* Update show/hidden status as the region view may have come from a hidden track,
757 or have moved to one.
759 if (current_tv->hidden ()) {
760 rv->get_canvas_group()->hide ();
762 rv->get_canvas_group()->show ();
765 /* Update the DraggingView */
766 i->time_axis_view += delta_time_axis_view;
767 i->layer += this_delta_layer;
770 _editor->mouse_brush_insert_region (rv, pending_region_position);
774 /* Get the y coordinate of the top of the track that this region is now over */
775 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
777 /* And adjust for the layer that it should be on */
778 StreamView* cv = current_tv->view ();
779 switch (cv->layer_display ()) {
783 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
786 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
790 /* need to get the parent of the regionview
791 * canvas group and get its position in
792 * equivalent coordinate space as the trackview
793 * we are now dragging over.
796 /* Now move the region view */
797 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0, 0)).y);
801 /* Only move the region into the empty dropzone at the bottom if the pointer
805 if (_drags->current_pointer_y() >= _editor->get_trackview_group()->item_to_canvas (Duple (0,0)).y) {
808 TimeAxisView* last = _time_axis_views.back();
809 track_origin = last->canvas_display()->item_to_canvas (track_origin);
810 track_origin.y += last->effective_height();
811 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0,0)).y);
812 i->time_axis_view = -1;
816 } /* foreach region */
818 _total_x_delta += x_delta;
820 if (x_delta != 0 && !_brushing) {
821 show_verbose_cursor_time (_last_frame_position);
824 _last_pointer_time_axis_view += delta_time_axis_view;
825 _last_pointer_layer += delta_layer;
829 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
831 if (_copy && first_move) {
833 /* duplicate the regionview(s) and region(s) */
835 list<DraggingView> new_regionviews;
837 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
839 RegionView* rv = i->view;
840 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
841 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
843 const boost::shared_ptr<const Region> original = rv->region();
844 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
845 region_copy->set_position (original->position());
849 boost::shared_ptr<AudioRegion> audioregion_copy
850 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
852 nrv = new AudioRegionView (*arv, audioregion_copy);
854 boost::shared_ptr<MidiRegion> midiregion_copy
855 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
856 nrv = new MidiRegionView (*mrv, midiregion_copy);
861 nrv->get_canvas_group()->show ();
862 new_regionviews.push_back (DraggingView (nrv, this));
864 /* swap _primary to the copy */
866 if (rv == _primary) {
870 /* ..and deselect the one we copied */
872 rv->set_selected (false);
875 if (!new_regionviews.empty()) {
877 /* reflect the fact that we are dragging the copies */
879 _views = new_regionviews;
881 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
885 RegionMotionDrag::motion (event, first_move);
889 RegionMotionDrag::finished (GdkEvent *, bool)
891 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
896 if ((*i)->view()->layer_display() == Expanded) {
897 (*i)->view()->set_layer_display (Stacked);
903 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
905 RegionMotionDrag::finished (ev, movement_occurred);
907 if (!movement_occurred) {
911 if (was_double_click() && !_views.empty()) {
912 DraggingView dv = _views.front();
913 dv.view->show_region_editor ();
920 /* reverse this here so that we have the correct logic to finalize
924 if (Config->get_edit_mode() == Lock) {
925 _x_constrained = !_x_constrained;
928 assert (!_views.empty ());
930 /* We might have hidden region views so that they weren't visible during the drag
931 (when they have been reparented). Now everything can be shown again, as region
932 views are back in their track parent groups.
934 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
935 i->view->get_canvas_group()->show ();
938 bool const changed_position = (_last_frame_position != _primary->region()->position());
939 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
940 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
960 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
964 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
966 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
971 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
972 list<boost::shared_ptr<AudioTrack> > audio_tracks;
973 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
974 return _editor->axis_view_from_route (audio_tracks.front());
976 ChanCount one_midi_port (DataType::MIDI, 1);
977 list<boost::shared_ptr<MidiTrack> > midi_tracks;
978 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
979 return _editor->axis_view_from_route (midi_tracks.front());
982 error << _("Could not create new track after region placed in the drop zone") << endmsg;
988 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
990 RegionSelection new_views;
991 PlaylistSet modified_playlists;
992 RouteTimeAxisView* new_time_axis_view = 0;
995 /* all changes were made during motion event handlers */
997 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1001 _editor->commit_reversible_command ();
1005 if (_x_constrained) {
1006 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1008 _editor->begin_reversible_command (Operations::region_copy);
1011 /* insert the regions into their new playlists */
1012 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1014 RouteTimeAxisView* dest_rtv = 0;
1016 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1022 if (changed_position && !_x_constrained) {
1023 where = i->view->region()->position() - drag_delta;
1025 where = i->view->region()->position();
1028 if (i->time_axis_view < 0) {
1029 if (!new_time_axis_view) {
1030 new_time_axis_view = create_destination_time_axis (i->view->region());
1032 dest_rtv = new_time_axis_view;
1034 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1037 if (dest_rtv != 0) {
1038 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1039 if (new_view != 0) {
1040 new_views.push_back (new_view);
1044 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1045 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1048 list<DraggingView>::const_iterator next = i;
1054 /* If we've created new regions either by copying or moving
1055 to a new track, we want to replace the old selection with the new ones
1058 if (new_views.size() > 0) {
1059 _editor->selection->set (new_views);
1062 /* write commands for the accumulated diffs for all our modified playlists */
1063 add_stateful_diff_commands_for_playlists (modified_playlists);
1065 _editor->commit_reversible_command ();
1069 RegionMoveDrag::finished_no_copy (
1070 bool const changed_position,
1071 bool const changed_tracks,
1072 framecnt_t const drag_delta
1075 RegionSelection new_views;
1076 PlaylistSet modified_playlists;
1077 PlaylistSet frozen_playlists;
1078 set<RouteTimeAxisView*> views_to_update;
1079 RouteTimeAxisView* new_time_axis_view = 0;
1082 /* all changes were made during motion event handlers */
1083 _editor->commit_reversible_command ();
1087 if (_x_constrained) {
1088 _editor->begin_reversible_command (_("fixed time region drag"));
1090 _editor->begin_reversible_command (Operations::region_drag);
1093 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1095 RegionView* rv = i->view;
1096 RouteTimeAxisView* dest_rtv = 0;
1098 if (rv->region()->locked() || rv->region()->video_locked()) {
1103 if (i->time_axis_view < 0) {
1104 if (!new_time_axis_view) {
1105 new_time_axis_view = create_destination_time_axis (rv->region());
1107 dest_rtv = new_time_axis_view;
1109 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1114 double const dest_layer = i->layer;
1116 views_to_update.insert (dest_rtv);
1120 if (changed_position && !_x_constrained) {
1121 where = rv->region()->position() - drag_delta;
1123 where = rv->region()->position();
1126 if (changed_tracks) {
1128 /* insert into new playlist */
1130 RegionView* new_view = insert_region_into_playlist (
1131 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1134 if (new_view == 0) {
1139 new_views.push_back (new_view);
1141 /* remove from old playlist */
1143 /* the region that used to be in the old playlist is not
1144 moved to the new one - we use a copy of it. as a result,
1145 any existing editor for the region should no longer be
1148 rv->hide_region_editor();
1149 rv->fake_set_opaque (false);
1151 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1155 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1157 /* this movement may result in a crossfade being modified, or a layering change,
1158 so we need to get undo data from the playlist as well as the region.
1161 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1163 playlist->clear_changes ();
1166 rv->region()->clear_changes ();
1169 motion on the same track. plonk the previously reparented region
1170 back to its original canvas group (its streamview).
1171 No need to do anything for copies as they are fake regions which will be deleted.
1174 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1175 rv->get_canvas_group()->set_y_position (i->initial_y);
1178 /* just change the model */
1179 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1180 playlist->set_layer (rv->region(), dest_layer);
1183 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1185 r = frozen_playlists.insert (playlist);
1188 playlist->freeze ();
1191 rv->region()->set_position (where);
1193 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1196 if (changed_tracks) {
1198 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1199 was selected in all of them, then removing it from a playlist will have removed all
1200 trace of it from _views (i.e. there were N regions selected, we removed 1,
1201 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1202 corresponding regionview, and _views is now empty).
1204 This could have invalidated any and all iterators into _views.
1206 The heuristic we use here is: if the region selection is empty, break out of the loop
1207 here. if the region selection is not empty, then restart the loop because we know that
1208 we must have removed at least the region(view) we've just been working on as well as any
1209 that we processed on previous iterations.
1211 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1212 we can just iterate.
1216 if (_views.empty()) {
1227 /* If we've created new regions either by copying or moving
1228 to a new track, we want to replace the old selection with the new ones
1231 if (new_views.size() > 0) {
1232 _editor->selection->set (new_views);
1235 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1239 /* write commands for the accumulated diffs for all our modified playlists */
1240 add_stateful_diff_commands_for_playlists (modified_playlists);
1242 _editor->commit_reversible_command ();
1244 /* We have futzed with the layering of canvas items on our streamviews.
1245 If any region changed layer, this will have resulted in the stream
1246 views being asked to set up their region views, and all will be well.
1247 If not, we might now have badly-ordered region views. Ask the StreamViews
1248 involved to sort themselves out, just in case.
1251 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1252 (*i)->view()->playlist_layered ((*i)->track ());
1256 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1257 * @param region Region to remove.
1258 * @param playlist playlist To remove from.
1259 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1260 * that clear_changes () is only called once per playlist.
1263 RegionMoveDrag::remove_region_from_playlist (
1264 boost::shared_ptr<Region> region,
1265 boost::shared_ptr<Playlist> playlist,
1266 PlaylistSet& modified_playlists
1269 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1272 playlist->clear_changes ();
1275 playlist->remove_region (region);
1279 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1280 * clearing the playlist's diff history first if necessary.
1281 * @param region Region to insert.
1282 * @param dest_rtv Destination RouteTimeAxisView.
1283 * @param dest_layer Destination layer.
1284 * @param where Destination position.
1285 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1286 * that clear_changes () is only called once per playlist.
1287 * @return New RegionView, or 0 if no insert was performed.
1290 RegionMoveDrag::insert_region_into_playlist (
1291 boost::shared_ptr<Region> region,
1292 RouteTimeAxisView* dest_rtv,
1295 PlaylistSet& modified_playlists
1298 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1299 if (!dest_playlist) {
1303 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1304 _new_region_view = 0;
1305 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1307 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1308 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1310 dest_playlist->clear_changes ();
1313 dest_playlist->add_region (region, where);
1315 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1316 dest_playlist->set_layer (region, dest_layer);
1321 assert (_new_region_view);
1323 return _new_region_view;
1327 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1329 _new_region_view = rv;
1333 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1335 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1336 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1338 _editor->session()->add_command (c);
1347 RegionMoveDrag::aborted (bool movement_occurred)
1351 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1358 RegionMotionDrag::aborted (movement_occurred);
1363 RegionMotionDrag::aborted (bool)
1365 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1367 StreamView* sview = (*i)->view();
1370 if (sview->layer_display() == Expanded) {
1371 sview->set_layer_display (Stacked);
1376 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1377 RegionView* rv = i->view;
1378 TimeAxisView* tv = &(rv->get_time_axis_view ());
1379 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1381 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1382 rv->get_canvas_group()->set_y_position (0);
1384 rv->fake_set_opaque (false);
1385 rv->move (-_total_x_delta, 0);
1386 rv->set_height (rtv->view()->child_height ());
1390 /** @param b true to brush, otherwise false.
1391 * @param c true to make copies of the regions being moved, otherwise false.
1393 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1394 : RegionMotionDrag (e, i, p, v, b),
1397 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1400 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1401 if (rtv && rtv->is_track()) {
1402 speed = rtv->track()->speed ();
1405 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1409 RegionMoveDrag::setup_pointer_frame_offset ()
1411 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1414 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1415 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1417 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1419 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1420 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1422 _primary = v->view()->create_region_view (r, false, false);
1424 _primary->get_canvas_group()->show ();
1425 _primary->set_position (pos, 0);
1426 _views.push_back (DraggingView (_primary, this));
1428 _last_frame_position = pos;
1430 _item = _primary->get_canvas_group ();
1434 RegionInsertDrag::finished (GdkEvent *, bool)
1436 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1438 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1439 _primary->get_canvas_group()->set_y_position (0);
1441 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1443 _editor->begin_reversible_command (Operations::insert_region);
1444 playlist->clear_changes ();
1445 playlist->add_region (_primary->region (), _last_frame_position);
1446 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1447 _editor->commit_reversible_command ();
1455 RegionInsertDrag::aborted (bool)
1462 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1463 : RegionMoveDrag (e, i, p, v, false, false)
1465 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1468 struct RegionSelectionByPosition {
1469 bool operator() (RegionView*a, RegionView* b) {
1470 return a->region()->position () < b->region()->position();
1475 RegionSpliceDrag::motion (GdkEvent* event, bool)
1477 /* Which trackview is this ? */
1479 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1480 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1482 /* The region motion is only processed if the pointer is over
1486 if (!tv || !tv->is_track()) {
1487 /* To make sure we hide the verbose canvas cursor when the mouse is
1488 not held over and audiotrack.
1490 _editor->verbose_cursor()->hide ();
1496 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1502 RegionSelection copy (_editor->selection->regions);
1504 RegionSelectionByPosition cmp;
1507 framepos_t const pf = adjusted_current_frame (event);
1509 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1511 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1517 boost::shared_ptr<Playlist> playlist;
1519 if ((playlist = atv->playlist()) == 0) {
1523 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1528 if (pf < (*i)->region()->last_frame() + 1) {
1532 if (pf > (*i)->region()->first_frame()) {
1538 playlist->shuffle ((*i)->region(), dir);
1543 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1545 RegionMoveDrag::finished (event, movement_occurred);
1549 RegionSpliceDrag::aborted (bool)
1554 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1556 _view (dynamic_cast<MidiTimeAxisView*> (v))
1558 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1564 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1567 _region = add_midi_region (_view);
1568 _view->playlist()->freeze ();
1571 framepos_t const f = adjusted_current_frame (event);
1572 if (f < grab_frame()) {
1573 _region->set_position (f);
1576 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1577 so that if this region is duplicated, its duplicate starts on
1578 a snap point rather than 1 frame after a snap point. Otherwise things get
1579 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1580 place snapped notes at the start of the region.
1583 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1584 _region->set_length (len < 1 ? 1 : len);
1590 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1592 if (!movement_occurred) {
1593 add_midi_region (_view);
1595 _view->playlist()->thaw ();
1600 RegionCreateDrag::aborted (bool)
1603 _view->playlist()->thaw ();
1609 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1613 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1617 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1619 Gdk::Cursor* cursor;
1620 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1622 float x_fraction = cnote->mouse_x_fraction ();
1624 if (x_fraction > 0.0 && x_fraction < 0.25) {
1625 cursor = _editor->cursors()->left_side_trim;
1627 cursor = _editor->cursors()->right_side_trim;
1630 Drag::start_grab (event, cursor);
1632 region = &cnote->region_view();
1634 double const region_start = region->get_position_pixels();
1635 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1637 if (grab_x() <= middle_point) {
1638 cursor = _editor->cursors()->left_side_trim;
1641 cursor = _editor->cursors()->right_side_trim;
1647 if (event->motion.state & Keyboard::PrimaryModifier) {
1653 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1655 if (ms.size() > 1) {
1656 /* has to be relative, may make no sense otherwise */
1660 /* select this note; if it is already selected, preserve the existing selection,
1661 otherwise make this note the only one selected.
1663 region->note_selected (cnote, cnote->selected ());
1665 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1666 MidiRegionSelection::iterator next;
1669 (*r)->begin_resizing (at_front);
1675 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1677 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1678 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1679 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1681 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1686 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1688 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1689 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1690 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1692 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1697 NoteResizeDrag::aborted (bool)
1699 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1700 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1701 (*r)->abort_resizing ();
1705 AVDraggingView::AVDraggingView (RegionView* v)
1708 initial_position = v->region()->position ();
1711 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1714 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1717 TrackViewList empty;
1719 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1720 std::list<RegionView*> views = rs.by_layer();
1722 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1723 RegionView* rv = (*i);
1724 if (!rv->region()->video_locked()) {
1727 _views.push_back (AVDraggingView (rv));
1732 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1734 Drag::start_grab (event);
1735 if (_editor->session() == 0) {
1739 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1740 _max_backwards_drag = (
1741 ARDOUR_UI::instance()->video_timeline->get_duration()
1742 + ARDOUR_UI::instance()->video_timeline->get_offset()
1743 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1746 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1747 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1748 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1751 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1754 Timecode::Time timecode;
1755 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1756 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);
1757 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1758 _editor->verbose_cursor()->show ();
1762 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1764 if (_editor->session() == 0) {
1767 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1771 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1772 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1774 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1775 dt = - _max_backwards_drag;
1778 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1779 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1781 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1782 RegionView* rv = i->view;
1783 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1786 rv->fake_set_opaque (true);
1787 rv->region()->clear_changes ();
1788 rv->region()->suspend_property_changes();
1790 rv->region()->set_position(i->initial_position + dt);
1791 rv->region_changed(ARDOUR::Properties::position);
1794 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1795 Timecode::Time timecode;
1796 Timecode::Time timediff;
1798 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1799 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1800 snprintf (buf, sizeof (buf),
1801 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1802 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1803 , _("Video Start:"),
1804 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1806 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1808 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1809 _editor->verbose_cursor()->show ();
1813 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1815 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1819 if (!movement_occurred || ! _editor->session()) {
1823 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1825 _editor->begin_reversible_command (_("Move Video"));
1827 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1828 ARDOUR_UI::instance()->video_timeline->save_undo();
1829 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1830 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1832 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1833 i->view->drag_end();
1834 i->view->fake_set_opaque (false);
1835 i->view->region()->resume_property_changes ();
1837 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1840 _editor->session()->maybe_update_session_range(
1841 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1842 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1846 _editor->commit_reversible_command ();
1850 VideoTimeLineDrag::aborted (bool)
1852 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1855 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1856 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1858 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1859 i->view->region()->resume_property_changes ();
1860 i->view->region()->set_position(i->initial_position);
1864 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1865 : RegionDrag (e, i, p, v)
1866 , _preserve_fade_anchor (preserve_fade_anchor)
1867 , _jump_position_when_done (false)
1869 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1873 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1876 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1877 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1879 if (tv && tv->is_track()) {
1880 speed = tv->track()->speed();
1883 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1884 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1885 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1887 framepos_t const pf = adjusted_current_frame (event);
1889 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1890 /* Move the contents of the region around without changing the region bounds */
1891 _operation = ContentsTrim;
1892 Drag::start_grab (event, _editor->cursors()->trimmer);
1894 /* These will get overridden for a point trim.*/
1895 if (pf < (region_start + region_length/2)) {
1896 /* closer to front */
1897 _operation = StartTrim;
1898 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1901 _operation = EndTrim;
1902 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1906 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1907 _jump_position_when_done = true;
1910 switch (_operation) {
1912 show_verbose_cursor_time (region_start);
1913 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1914 i->view->trim_front_starting ();
1918 show_verbose_cursor_time (region_end);
1921 show_verbose_cursor_time (pf);
1925 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1926 i->view->region()->suspend_property_changes ();
1931 TrimDrag::motion (GdkEvent* event, bool first_move)
1933 RegionView* rv = _primary;
1936 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1937 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1938 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1939 frameoffset_t frame_delta = 0;
1941 if (tv && tv->is_track()) {
1942 speed = tv->track()->speed();
1945 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1951 switch (_operation) {
1953 trim_type = "Region start trim";
1956 trim_type = "Region end trim";
1959 trim_type = "Region content trim";
1966 _editor->begin_reversible_command (trim_type);
1968 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1969 RegionView* rv = i->view;
1970 rv->fake_set_opaque (false);
1971 rv->enable_display (false);
1972 rv->region()->playlist()->clear_owned_changes ();
1974 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1977 arv->temporarily_hide_envelope ();
1981 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1982 insert_result = _editor->motion_frozen_playlists.insert (pl);
1984 if (insert_result.second) {
1990 bool non_overlap_trim = false;
1992 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1993 non_overlap_trim = true;
1996 /* contstrain trim to fade length */
1997 if (_preserve_fade_anchor) {
1998 switch (_operation) {
2000 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2001 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2003 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2004 if (ar->locked()) continue;
2005 framecnt_t len = ar->fade_in()->back()->when;
2006 if (len < dt) dt = min(dt, len);
2010 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2011 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2013 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2014 if (ar->locked()) continue;
2015 framecnt_t len = ar->fade_out()->back()->when;
2016 if (len < -dt) dt = max(dt, -len);
2025 switch (_operation) {
2027 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2028 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2029 if (changed && _preserve_fade_anchor) {
2030 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2032 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2033 framecnt_t len = ar->fade_in()->back()->when;
2034 framecnt_t diff = ar->first_frame() - i->initial_position;
2035 framepos_t new_length = len - diff;
2036 i->anchored_fade_length = min (ar->length(), new_length);
2037 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2038 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2045 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2046 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2047 if (changed && _preserve_fade_anchor) {
2048 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2050 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2051 framecnt_t len = ar->fade_out()->back()->when;
2052 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2053 framepos_t new_length = len + diff;
2054 i->anchored_fade_length = min (ar->length(), new_length);
2055 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2056 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2064 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2066 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2067 i->view->move_contents (frame_delta);
2073 switch (_operation) {
2075 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2078 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2081 // show_verbose_cursor_time (frame_delta);
2088 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2090 if (movement_occurred) {
2091 motion (event, false);
2093 if (_operation == StartTrim) {
2094 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2096 /* This must happen before the region's StatefulDiffCommand is created, as it may
2097 `correct' (ahem) the region's _start from being negative to being zero. It
2098 needs to be zero in the undo record.
2100 i->view->trim_front_ending ();
2102 if (_preserve_fade_anchor) {
2103 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2105 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2106 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2107 ar->set_fade_in_length(i->anchored_fade_length);
2108 ar->set_fade_in_active(true);
2111 if (_jump_position_when_done) {
2112 i->view->region()->set_position (i->initial_position);
2115 } else if (_operation == EndTrim) {
2116 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2117 if (_preserve_fade_anchor) {
2118 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2120 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2121 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2122 ar->set_fade_out_length(i->anchored_fade_length);
2123 ar->set_fade_out_active(true);
2126 if (_jump_position_when_done) {
2127 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2132 if (!_views.empty()) {
2133 if (_operation == StartTrim) {
2134 _editor->maybe_locate_with_edit_preroll(
2135 _views.begin()->view->region()->position());
2137 if (_operation == EndTrim) {
2138 _editor->maybe_locate_with_edit_preroll(
2139 _views.begin()->view->region()->position() +
2140 _views.begin()->view->region()->length());
2144 if (!_editor->selection->selected (_primary)) {
2145 _primary->thaw_after_trim ();
2148 set<boost::shared_ptr<Playlist> > diffed_playlists;
2150 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2151 i->view->thaw_after_trim ();
2152 i->view->enable_display (true);
2153 i->view->fake_set_opaque (true);
2155 /* Trimming one region may affect others on the playlist, so we need
2156 to get undo Commands from the whole playlist rather than just the
2157 region. Use diffed_playlists to make sure we don't diff a given
2158 playlist more than once.
2160 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2161 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2162 vector<Command*> cmds;
2164 _editor->session()->add_commands (cmds);
2165 diffed_playlists.insert (p);
2170 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2174 _editor->motion_frozen_playlists.clear ();
2175 _editor->commit_reversible_command();
2178 /* no mouse movement */
2179 _editor->point_trim (event, adjusted_current_frame (event));
2182 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2183 if (_operation == StartTrim) {
2184 i->view->trim_front_ending ();
2187 i->view->region()->resume_property_changes ();
2192 TrimDrag::aborted (bool movement_occurred)
2194 /* Our motion method is changing model state, so use the Undo system
2195 to cancel. Perhaps not ideal, as this will leave an Undo point
2196 behind which may be slightly odd from the user's point of view.
2201 if (movement_occurred) {
2205 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2206 i->view->region()->resume_property_changes ();
2211 TrimDrag::setup_pointer_frame_offset ()
2213 list<DraggingView>::iterator i = _views.begin ();
2214 while (i != _views.end() && i->view != _primary) {
2218 if (i == _views.end()) {
2222 switch (_operation) {
2224 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2227 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2234 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2238 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2239 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2244 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2246 Drag::start_grab (event, cursor);
2247 show_verbose_cursor_time (adjusted_current_frame(event));
2251 MeterMarkerDrag::setup_pointer_frame_offset ()
2253 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2257 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2259 if (!_marker->meter().movable()) {
2265 // create a dummy marker for visual representation of moving the
2266 // section, because whether its a copy or not, we're going to
2267 // leave or lose the original marker (leave if its a copy; lose if its
2268 // not, because we'll remove it from the map).
2270 MeterSection section (_marker->meter());
2272 if (!section.movable()) {
2277 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2279 _marker = new MeterMarker (
2281 *_editor->meter_group,
2282 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2284 *new MeterSection (_marker->meter())
2287 /* use the new marker for the grab */
2288 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2291 TempoMap& map (_editor->session()->tempo_map());
2292 /* get current state */
2293 before_state = &map.get_state();
2294 /* remove the section while we drag it */
2295 map.remove_meter (section, true);
2299 framepos_t const pf = adjusted_current_frame (event);
2300 _marker->set_position (pf);
2301 show_verbose_cursor_time (pf);
2305 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2307 if (!movement_occurred) {
2308 if (was_double_click()) {
2309 _editor->edit_meter_marker (*_marker);
2314 if (!_marker->meter().movable()) {
2318 motion (event, false);
2320 Timecode::BBT_Time when;
2322 TempoMap& map (_editor->session()->tempo_map());
2323 map.bbt_time (last_pointer_frame(), when);
2325 if (_copy == true) {
2326 _editor->begin_reversible_command (_("copy meter mark"));
2327 XMLNode &before = map.get_state();
2328 map.add_meter (_marker->meter(), when);
2329 XMLNode &after = map.get_state();
2330 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2331 _editor->commit_reversible_command ();
2334 _editor->begin_reversible_command (_("move meter mark"));
2336 /* we removed it before, so add it back now */
2338 map.add_meter (_marker->meter(), when);
2339 XMLNode &after = map.get_state();
2340 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2341 _editor->commit_reversible_command ();
2344 // delete the dummy marker we used for visual representation while moving.
2345 // a new visual marker will show up automatically.
2350 MeterMarkerDrag::aborted (bool moved)
2352 _marker->set_position (_marker->meter().frame ());
2355 TempoMap& map (_editor->session()->tempo_map());
2356 /* we removed it before, so add it back now */
2357 map.add_meter (_marker->meter(), _marker->meter().frame());
2358 // delete the dummy marker we used for visual representation while moving.
2359 // a new visual marker will show up automatically.
2364 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2368 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2370 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2375 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2377 Drag::start_grab (event, cursor);
2378 show_verbose_cursor_time (adjusted_current_frame (event));
2382 TempoMarkerDrag::setup_pointer_frame_offset ()
2384 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2388 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2390 if (!_marker->tempo().movable()) {
2396 // create a dummy marker for visual representation of moving the
2397 // section, because whether its a copy or not, we're going to
2398 // leave or lose the original marker (leave if its a copy; lose if its
2399 // not, because we'll remove it from the map).
2401 // create a dummy marker for visual representation of moving the copy.
2402 // The actual copying is not done before we reach the finish callback.
2405 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2407 TempoSection section (_marker->tempo());
2409 _marker = new TempoMarker (
2411 *_editor->tempo_group,
2412 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2414 *new TempoSection (_marker->tempo())
2417 /* use the new marker for the grab */
2418 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2421 TempoMap& map (_editor->session()->tempo_map());
2422 /* get current state */
2423 before_state = &map.get_state();
2424 /* remove the section while we drag it */
2425 map.remove_tempo (section, true);
2429 framepos_t const pf = adjusted_current_frame (event);
2430 _marker->set_position (pf);
2431 show_verbose_cursor_time (pf);
2435 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2437 if (!movement_occurred) {
2438 if (was_double_click()) {
2439 _editor->edit_tempo_marker (*_marker);
2444 if (!_marker->tempo().movable()) {
2448 motion (event, false);
2450 TempoMap& map (_editor->session()->tempo_map());
2451 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2452 Timecode::BBT_Time when;
2454 map.bbt_time (beat_time, when);
2456 if (_copy == true) {
2457 _editor->begin_reversible_command (_("copy tempo mark"));
2458 XMLNode &before = map.get_state();
2459 map.add_tempo (_marker->tempo(), when);
2460 XMLNode &after = map.get_state();
2461 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2462 _editor->commit_reversible_command ();
2465 _editor->begin_reversible_command (_("move tempo mark"));
2466 /* we removed it before, so add it back now */
2467 map.add_tempo (_marker->tempo(), when);
2468 XMLNode &after = map.get_state();
2469 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2470 _editor->commit_reversible_command ();
2473 // delete the dummy marker we used for visual representation while moving.
2474 // a new visual marker will show up automatically.
2479 TempoMarkerDrag::aborted (bool moved)
2481 _marker->set_position (_marker->tempo().frame());
2483 TempoMap& map (_editor->session()->tempo_map());
2484 /* we removed it before, so add it back now */
2485 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2486 // delete the dummy marker we used for visual representation while moving.
2487 // a new visual marker will show up automatically.
2492 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2493 : Drag (e, &c.track_canvas_item())
2497 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2500 /** Do all the things we do when dragging the playhead to make it look as though
2501 * we have located, without actually doing the locate (because that would cause
2502 * the diskstream buffers to be refilled, which is too slow).
2505 CursorDrag::fake_locate (framepos_t t)
2507 _editor->playhead_cursor->set_position (t);
2509 Session* s = _editor->session ();
2510 if (s->timecode_transmission_suspended ()) {
2511 framepos_t const f = _editor->playhead_cursor->current_frame ();
2512 /* This is asynchronous so it will be sent "now"
2514 s->send_mmc_locate (f);
2515 /* These are synchronous and will be sent during the next
2518 s->queue_full_time_code ();
2519 s->queue_song_position_pointer ();
2522 show_verbose_cursor_time (t);
2523 _editor->UpdateAllTransportClocks (t);
2527 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2529 Drag::start_grab (event, c);
2531 _grab_zoom = _editor->samples_per_pixel;
2533 framepos_t where = _editor->canvas_event_sample (event);
2535 _editor->snap_to_with_modifier (where, event);
2537 _editor->_dragging_playhead = true;
2539 Session* s = _editor->session ();
2541 /* grab the track canvas item as well */
2543 _cursor.track_canvas_item().grab();
2546 if (_was_rolling && _stop) {
2550 if (s->is_auditioning()) {
2551 s->cancel_audition ();
2555 if (AudioEngine::instance()->connected()) {
2557 /* do this only if we're the engine is connected
2558 * because otherwise this request will never be
2559 * serviced and we'll busy wait forever. likewise,
2560 * notice if we are disconnected while waiting for the
2561 * request to be serviced.
2564 s->request_suspend_timecode_transmission ();
2565 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2566 /* twiddle our thumbs */
2571 fake_locate (where);
2575 CursorDrag::motion (GdkEvent* event, bool)
2577 framepos_t const adjusted_frame = adjusted_current_frame (event);
2578 if (adjusted_frame != last_pointer_frame()) {
2579 fake_locate (adjusted_frame);
2584 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2586 _editor->_dragging_playhead = false;
2588 _cursor.track_canvas_item().ungrab();
2590 if (!movement_occurred && _stop) {
2594 motion (event, false);
2596 Session* s = _editor->session ();
2598 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2599 _editor->_pending_locate_request = true;
2600 s->request_resume_timecode_transmission ();
2605 CursorDrag::aborted (bool)
2607 _cursor.track_canvas_item().ungrab();
2609 if (_editor->_dragging_playhead) {
2610 _editor->session()->request_resume_timecode_transmission ();
2611 _editor->_dragging_playhead = false;
2614 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2617 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2618 : RegionDrag (e, i, p, v)
2620 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2624 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2626 Drag::start_grab (event, cursor);
2628 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2629 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2631 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2635 FadeInDrag::setup_pointer_frame_offset ()
2637 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2638 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2639 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2643 FadeInDrag::motion (GdkEvent* event, bool)
2645 framecnt_t fade_length;
2647 framepos_t const pos = adjusted_current_frame (event);
2649 boost::shared_ptr<Region> region = _primary->region ();
2651 if (pos < (region->position() + 64)) {
2652 fade_length = 64; // this should be a minimum defined somewhere
2653 } else if (pos > region->last_frame()) {
2654 fade_length = region->length();
2656 fade_length = pos - region->position();
2659 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2661 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2667 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2670 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2674 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2676 if (!movement_occurred) {
2680 framecnt_t fade_length;
2682 framepos_t const pos = adjusted_current_frame (event);
2684 boost::shared_ptr<Region> region = _primary->region ();
2686 if (pos < (region->position() + 64)) {
2687 fade_length = 64; // this should be a minimum defined somewhere
2688 } else if (pos > region->last_frame()) {
2689 fade_length = region->length();
2691 fade_length = pos - region->position();
2694 _editor->begin_reversible_command (_("change fade in length"));
2696 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2698 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2704 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2705 XMLNode &before = alist->get_state();
2707 tmp->audio_region()->set_fade_in_length (fade_length);
2708 tmp->audio_region()->set_fade_in_active (true);
2710 XMLNode &after = alist->get_state();
2711 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2714 _editor->commit_reversible_command ();
2718 FadeInDrag::aborted (bool)
2720 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2721 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2727 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2731 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2732 : RegionDrag (e, i, p, v)
2734 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2738 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2740 Drag::start_grab (event, cursor);
2742 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2743 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2745 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2749 FadeOutDrag::setup_pointer_frame_offset ()
2751 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2752 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2753 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2757 FadeOutDrag::motion (GdkEvent* event, bool)
2759 framecnt_t fade_length;
2761 framepos_t const pos = adjusted_current_frame (event);
2763 boost::shared_ptr<Region> region = _primary->region ();
2765 if (pos > (region->last_frame() - 64)) {
2766 fade_length = 64; // this should really be a minimum fade defined somewhere
2768 else if (pos < region->position()) {
2769 fade_length = region->length();
2772 fade_length = region->last_frame() - pos;
2775 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2777 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2783 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2786 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2790 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2792 if (!movement_occurred) {
2796 framecnt_t fade_length;
2798 framepos_t const pos = adjusted_current_frame (event);
2800 boost::shared_ptr<Region> region = _primary->region ();
2802 if (pos > (region->last_frame() - 64)) {
2803 fade_length = 64; // this should really be a minimum fade defined somewhere
2805 else if (pos < region->position()) {
2806 fade_length = region->length();
2809 fade_length = region->last_frame() - pos;
2812 _editor->begin_reversible_command (_("change fade out length"));
2814 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2816 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2822 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2823 XMLNode &before = alist->get_state();
2825 tmp->audio_region()->set_fade_out_length (fade_length);
2826 tmp->audio_region()->set_fade_out_active (true);
2828 XMLNode &after = alist->get_state();
2829 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2832 _editor->commit_reversible_command ();
2836 FadeOutDrag::aborted (bool)
2838 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2839 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2845 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2849 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2852 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2854 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2857 _points.push_back (ArdourCanvas::Duple (0, 0));
2858 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2861 MarkerDrag::~MarkerDrag ()
2863 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2868 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2870 location = new Location (*l);
2871 markers.push_back (m);
2876 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2878 Drag::start_grab (event, cursor);
2882 Location *location = _editor->find_location_from_marker (_marker, is_start);
2883 _editor->_dragging_edit_point = true;
2885 update_item (location);
2887 // _drag_line->show();
2888 // _line->raise_to_top();
2891 show_verbose_cursor_time (location->start());
2893 show_verbose_cursor_time (location->end());
2896 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2899 case Selection::Toggle:
2900 /* we toggle on the button release */
2902 case Selection::Set:
2903 if (!_editor->selection->selected (_marker)) {
2904 _editor->selection->set (_marker);
2907 case Selection::Extend:
2909 Locations::LocationList ll;
2910 list<Marker*> to_add;
2912 _editor->selection->markers.range (s, e);
2913 s = min (_marker->position(), s);
2914 e = max (_marker->position(), e);
2917 if (e < max_framepos) {
2920 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2921 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2922 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2925 to_add.push_back (lm->start);
2928 to_add.push_back (lm->end);
2932 if (!to_add.empty()) {
2933 _editor->selection->add (to_add);
2937 case Selection::Add:
2938 _editor->selection->add (_marker);
2942 /* Set up copies for us to manipulate during the drag
2945 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2947 Location* l = _editor->find_location_from_marker (*i, is_start);
2954 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2956 /* range: check that the other end of the range isn't
2959 CopiedLocationInfo::iterator x;
2960 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2961 if (*(*x).location == *l) {
2965 if (x == _copied_locations.end()) {
2966 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2968 (*x).markers.push_back (*i);
2969 (*x).move_both = true;
2977 MarkerDrag::setup_pointer_frame_offset ()
2980 Location *location = _editor->find_location_from_marker (_marker, is_start);
2981 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2985 MarkerDrag::motion (GdkEvent* event, bool)
2987 framecnt_t f_delta = 0;
2989 bool move_both = false;
2990 Location *real_location;
2991 Location *copy_location = 0;
2993 framepos_t const newframe = adjusted_current_frame (event);
2994 framepos_t next = newframe;
2996 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3000 CopiedLocationInfo::iterator x;
3002 /* find the marker we're dragging, and compute the delta */
3004 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3006 copy_location = (*x).location;
3008 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3010 /* this marker is represented by this
3011 * CopiedLocationMarkerInfo
3014 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3019 if (real_location->is_mark()) {
3020 f_delta = newframe - copy_location->start();
3024 switch (_marker->type()) {
3025 case Marker::SessionStart:
3026 case Marker::RangeStart:
3027 case Marker::LoopStart:
3028 case Marker::PunchIn:
3029 f_delta = newframe - copy_location->start();
3032 case Marker::SessionEnd:
3033 case Marker::RangeEnd:
3034 case Marker::LoopEnd:
3035 case Marker::PunchOut:
3036 f_delta = newframe - copy_location->end();
3039 /* what kind of marker is this ? */
3048 if (x == _copied_locations.end()) {
3049 /* hmm, impossible - we didn't find the dragged marker */
3053 /* now move them all */
3055 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3057 copy_location = x->location;
3059 /* call this to find out if its the start or end */
3061 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3065 if (real_location->locked()) {
3069 if (copy_location->is_mark()) {
3073 copy_location->set_start (copy_location->start() + f_delta);
3077 framepos_t new_start = copy_location->start() + f_delta;
3078 framepos_t new_end = copy_location->end() + f_delta;
3080 if (is_start) { // start-of-range marker
3082 if (move_both || (*x).move_both) {
3083 copy_location->set_start (new_start);
3084 copy_location->set_end (new_end);
3085 } else if (new_start < copy_location->end()) {
3086 copy_location->set_start (new_start);
3087 } else if (newframe > 0) {
3088 _editor->snap_to (next, 1, true);
3089 copy_location->set_end (next);
3090 copy_location->set_start (newframe);
3093 } else { // end marker
3095 if (move_both || (*x).move_both) {
3096 copy_location->set_end (new_end);
3097 copy_location->set_start (new_start);
3098 } else if (new_end > copy_location->start()) {
3099 copy_location->set_end (new_end);
3100 } else if (newframe > 0) {
3101 _editor->snap_to (next, -1, true);
3102 copy_location->set_start (next);
3103 copy_location->set_end (newframe);
3108 update_item (copy_location);
3110 /* now lookup the actual GUI items used to display this
3111 * location and move them to wherever the copy of the location
3112 * is now. This means that the logic in ARDOUR::Location is
3113 * still enforced, even though we are not (yet) modifying
3114 * the real Location itself.
3117 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3120 lm->set_position (copy_location->start(), copy_location->end());
3125 assert (!_copied_locations.empty());
3127 show_verbose_cursor_time (newframe);
3131 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3133 if (!movement_occurred) {
3135 if (was_double_click()) {
3136 _editor->rename_marker (_marker);
3140 /* just a click, do nothing but finish
3141 off the selection process
3144 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3147 case Selection::Set:
3148 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3149 _editor->selection->set (_marker);
3153 case Selection::Toggle:
3154 /* we toggle on the button release, click only */
3155 _editor->selection->toggle (_marker);
3158 case Selection::Extend:
3159 case Selection::Add:
3166 _editor->_dragging_edit_point = false;
3168 _editor->begin_reversible_command ( _("move marker") );
3169 XMLNode &before = _editor->session()->locations()->get_state();
3171 MarkerSelection::iterator i;
3172 CopiedLocationInfo::iterator x;
3175 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3176 x != _copied_locations.end() && i != _editor->selection->markers.end();
3179 Location * location = _editor->find_location_from_marker (*i, is_start);
3183 if (location->locked()) {
3187 if (location->is_mark()) {
3188 location->set_start (((*x).location)->start());
3190 location->set (((*x).location)->start(), ((*x).location)->end());
3195 XMLNode &after = _editor->session()->locations()->get_state();
3196 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3197 _editor->commit_reversible_command ();
3201 MarkerDrag::aborted (bool)
3207 MarkerDrag::update_item (Location*)
3212 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3214 _cumulative_x_drag (0),
3215 _cumulative_y_drag (0)
3217 if (_zero_gain_fraction < 0.0) {
3218 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3221 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3223 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3229 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3231 Drag::start_grab (event, _editor->cursors()->fader);
3233 // start the grab at the center of the control point so
3234 // the point doesn't 'jump' to the mouse after the first drag
3235 _fixed_grab_x = _point->get_x();
3236 _fixed_grab_y = _point->get_y();
3238 float const fraction = 1 - (_point->get_y() / _point->line().height());
3240 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3242 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3243 event->button.x + 10, event->button.y + 10);
3245 _editor->verbose_cursor()->show ();
3247 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3249 if (!_point->can_slide ()) {
3250 _x_constrained = true;
3255 ControlPointDrag::motion (GdkEvent* event, bool)
3257 double dx = _drags->current_pointer_x() - last_pointer_x();
3258 double dy = _drags->current_pointer_y() - last_pointer_y();
3260 if (event->button.state & Keyboard::SecondaryModifier) {
3265 /* coordinate in pixels relative to the start of the region (for region-based automation)
3266 or track (for track-based automation) */
3267 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3268 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3270 // calculate zero crossing point. back off by .01 to stay on the
3271 // positive side of zero
3272 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3274 // make sure we hit zero when passing through
3275 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3279 if (_x_constrained) {
3282 if (_y_constrained) {
3286 _cumulative_x_drag = cx - _fixed_grab_x;
3287 _cumulative_y_drag = cy - _fixed_grab_y;
3291 cy = min ((double) _point->line().height(), cy);
3293 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3295 if (!_x_constrained) {
3296 _editor->snap_to_with_modifier (cx_frames, event);
3299 cx_frames = min (cx_frames, _point->line().maximum_time());
3301 float const fraction = 1.0 - (cy / _point->line().height());
3303 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3305 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3309 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3311 if (!movement_occurred) {
3315 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3316 _editor->reset_point_selection ();
3320 motion (event, false);
3323 _point->line().end_drag (_pushing, _final_index);
3324 _editor->session()->commit_reversible_command ();
3328 ControlPointDrag::aborted (bool)
3330 _point->line().reset ();
3334 ControlPointDrag::active (Editing::MouseMode m)
3336 if (m == Editing::MouseGain) {
3337 /* always active in mouse gain */
3341 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3342 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3345 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3348 _cumulative_y_drag (0)
3350 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3354 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3356 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3359 _item = &_line->grab_item ();
3361 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3362 origin, and ditto for y.
3365 double cx = event->button.x;
3366 double cy = event->button.y;
3368 _line->parent_group().canvas_to_item (cx, cy);
3370 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3375 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3376 /* no adjacent points */
3380 Drag::start_grab (event, _editor->cursors()->fader);
3382 /* store grab start in parent frame */
3387 double fraction = 1.0 - (cy / _line->height());
3389 _line->start_drag_line (before, after, fraction);
3391 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3392 event->button.x + 10, event->button.y + 10);
3394 _editor->verbose_cursor()->show ();
3398 LineDrag::motion (GdkEvent* event, bool)
3400 double dy = _drags->current_pointer_y() - last_pointer_y();
3402 if (event->button.state & Keyboard::SecondaryModifier) {
3406 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3408 _cumulative_y_drag = cy - _fixed_grab_y;
3411 cy = min ((double) _line->height(), cy);
3413 double const fraction = 1.0 - (cy / _line->height());
3416 /* we are ignoring x position for this drag, so we can just pass in anything */
3417 _line->drag_motion (0, fraction, true, false, ignored);
3419 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3423 LineDrag::finished (GdkEvent* event, bool movement_occured)
3425 if (movement_occured) {
3426 motion (event, false);
3427 _line->end_drag (false, 0);
3429 /* add a new control point on the line */
3431 AutomationTimeAxisView* atv;
3433 _line->end_drag (false, 0);
3435 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3436 framepos_t where = _editor->window_event_sample (event, 0, 0);
3437 atv->add_automation_event (event, where, event->button.y, false);
3441 _editor->session()->commit_reversible_command ();
3445 LineDrag::aborted (bool)
3450 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3453 _cumulative_x_drag (0)
3455 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3459 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3461 Drag::start_grab (event);
3463 _line = reinterpret_cast<Line*> (_item);
3466 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3468 double cx = event->button.x;
3469 double cy = event->button.y;
3471 _item->parent()->canvas_to_item (cx, cy);
3473 /* store grab start in parent frame */
3474 _region_view_grab_x = cx;
3476 _before = *(float*) _item->get_data ("position");
3478 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3480 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3484 FeatureLineDrag::motion (GdkEvent*, bool)
3486 double dx = _drags->current_pointer_x() - last_pointer_x();
3488 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3490 _cumulative_x_drag += dx;
3492 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3501 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3503 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3505 float *pos = new float;
3508 _line->set_data ("position", pos);
3514 FeatureLineDrag::finished (GdkEvent*, bool)
3516 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3517 _arv->update_transient(_before, _before);
3521 FeatureLineDrag::aborted (bool)
3526 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3528 , _vertical_only (false)
3530 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3534 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3536 Drag::start_grab (event);
3537 show_verbose_cursor_time (adjusted_current_frame (event));
3541 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3548 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3550 framepos_t grab = grab_frame ();
3551 if (Config->get_rubberbanding_snaps_to_grid ()) {
3552 _editor->snap_to_with_modifier (grab, event);
3555 /* base start and end on initial click position */
3565 if (_drags->current_pointer_y() < grab_y()) {
3566 y1 = _drags->current_pointer_y();
3569 y2 = _drags->current_pointer_y();
3574 if (start != end || y1 != y2) {
3576 double x1 = _editor->sample_to_pixel (start);
3577 double x2 = _editor->sample_to_pixel (end);
3578 const double min_dimension = 2.0;
3580 _editor->rubberband_rect->set_x0 (x1);
3581 if (_vertical_only) {
3582 /* fixed 10 pixel width */
3583 _editor->rubberband_rect->set_x1 (x1 + 10);
3586 x2 = min (x1 - min_dimension, x2);
3588 x2 = max (x1 + min_dimension, x2);
3590 _editor->rubberband_rect->set_x1 (x2);
3593 _editor->rubberband_rect->set_y0 (y1);
3595 y2 = min (y1 - min_dimension, y2);
3597 y2 = max (y1 + min_dimension, y2);
3600 _editor->rubberband_rect->set_y1 (y2);
3602 _editor->rubberband_rect->show();
3603 _editor->rubberband_rect->raise_to_top();
3605 show_verbose_cursor_time (pf);
3607 do_select_things (event, true);
3612 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3617 if (grab_frame() < last_pointer_frame()) {
3619 x2 = last_pointer_frame ();
3622 x1 = last_pointer_frame ();
3628 if (_drags->current_pointer_y() < grab_y()) {
3629 y1 = _drags->current_pointer_y();
3632 y2 = _drags->current_pointer_y();
3636 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3640 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3642 if (movement_occurred) {
3644 motion (event, false);
3645 do_select_things (event, false);
3651 bool do_deselect = true;
3652 MidiTimeAxisView* mtv;
3654 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3656 if (_editor->selection->empty()) {
3657 /* nothing selected */
3658 add_midi_region (mtv);
3659 do_deselect = false;
3663 /* do not deselect if Primary or Tertiary (toggle-select or
3664 * extend-select are pressed.
3667 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3668 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3675 _editor->rubberband_rect->hide();
3679 RubberbandSelectDrag::aborted (bool)
3681 _editor->rubberband_rect->hide ();
3684 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3685 : RegionDrag (e, i, p, v)
3687 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3691 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3693 Drag::start_grab (event, cursor);
3695 show_verbose_cursor_time (adjusted_current_frame (event));
3699 TimeFXDrag::motion (GdkEvent* event, bool)
3701 RegionView* rv = _primary;
3702 StreamView* cv = rv->get_time_axis_view().view ();
3704 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3705 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3706 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3708 framepos_t const pf = adjusted_current_frame (event);
3710 if (pf > rv->region()->position()) {
3711 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3714 show_verbose_cursor_time (pf);
3718 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3720 _primary->get_time_axis_view().hide_timestretch ();
3722 if (!movement_occurred) {
3726 if (last_pointer_frame() < _primary->region()->position()) {
3727 /* backwards drag of the left edge - not usable */
3731 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3733 float percentage = (double) newlen / (double) _primary->region()->length();
3735 #ifndef USE_RUBBERBAND
3736 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3737 if (_primary->region()->data_type() == DataType::AUDIO) {
3738 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3742 if (!_editor->get_selection().regions.empty()) {
3743 /* primary will already be included in the selection, and edit
3744 group shared editing will propagate selection across
3745 equivalent regions, so just use the current region
3749 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3750 error << _("An error occurred while executing time stretch operation") << endmsg;
3756 TimeFXDrag::aborted (bool)
3758 _primary->get_time_axis_view().hide_timestretch ();
3761 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3764 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3768 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3770 Drag::start_grab (event);
3774 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3776 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3780 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3782 if (movement_occurred && _editor->session()) {
3783 /* make sure we stop */
3784 _editor->session()->request_transport_speed (0.0);
3789 ScrubDrag::aborted (bool)
3794 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3799 , _original_pointer_time_axis (-1)
3800 , _last_pointer_time_axis (-1)
3801 , _time_selection_at_start (!_editor->get_selection().time.empty())
3803 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3805 if (_time_selection_at_start) {
3806 start_at_start = _editor->get_selection().time.start();
3807 end_at_start = _editor->get_selection().time.end_frame();
3812 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3814 if (_editor->session() == 0) {
3818 Gdk::Cursor* cursor = 0;
3820 switch (_operation) {
3821 case CreateSelection:
3822 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3827 cursor = _editor->cursors()->selector;
3828 Drag::start_grab (event, cursor);
3831 case SelectionStartTrim:
3832 if (_editor->clicked_axisview) {
3833 _editor->clicked_axisview->order_selection_trims (_item, true);
3835 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3838 case SelectionEndTrim:
3839 if (_editor->clicked_axisview) {
3840 _editor->clicked_axisview->order_selection_trims (_item, false);
3842 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3846 Drag::start_grab (event, cursor);
3849 case SelectionExtend:
3850 Drag::start_grab (event, cursor);
3854 if (_operation == SelectionMove) {
3855 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3857 show_verbose_cursor_time (adjusted_current_frame (event));
3860 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3864 SelectionDrag::setup_pointer_frame_offset ()
3866 switch (_operation) {
3867 case CreateSelection:
3868 _pointer_frame_offset = 0;
3871 case SelectionStartTrim:
3873 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3876 case SelectionEndTrim:
3877 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3880 case SelectionExtend:
3886 SelectionDrag::motion (GdkEvent* event, bool first_move)
3888 framepos_t start = 0;
3890 framecnt_t length = 0;
3891 framecnt_t distance = 0;
3893 framepos_t const pending_position = adjusted_current_frame (event);
3895 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3899 switch (_operation) {
3900 case CreateSelection:
3902 framepos_t grab = grab_frame ();
3905 grab = adjusted_current_frame (event, false);
3906 if (grab < pending_position) {
3907 _editor->snap_to (grab, -1);
3909 _editor->snap_to (grab, 1);
3913 if (pending_position < grab) {
3914 start = pending_position;
3917 end = pending_position;
3921 /* first drag: Either add to the selection
3922 or create a new selection
3929 /* adding to the selection */
3930 _editor->set_selected_track_as_side_effect (Selection::Add);
3931 _editor->clicked_selection = _editor->selection->add (start, end);
3938 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3939 _editor->set_selected_track_as_side_effect (Selection::Set);
3942 _editor->clicked_selection = _editor->selection->set (start, end);
3946 /* select all tracks within the rectangle that we've marked out so far */
3947 TrackViewList to_be_added_to_selection;
3948 TrackViewList to_be_removed_from_selection;
3949 TrackViewList& all_tracks (_editor->track_views);
3951 // /* convert grab_y and current_pointer_y into offsets within the trackview group */
3953 ArdourCanvas::Duple const top_of_trackviews_canvas = _editor->get_trackview_group()->item_to_canvas (ArdourCanvas::Duple (0, 0));
3954 ArdourCanvas::Coord const top = grab_y() - top_of_trackviews_canvas.y;
3955 ArdourCanvas::Coord const bottom = _drags->current_pointer_y() - top_of_trackviews_canvas.y;
3957 if (top >= 0 && bottom >= 0) {
3959 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3961 if ((*i)->covered_by_y_range (top, bottom)) {
3962 if (!(*i)->get_selected()) {
3963 to_be_added_to_selection.push_back (*i);
3966 if ((*i)->get_selected()) {
3967 to_be_removed_from_selection.push_back (*i);
3972 if (!to_be_added_to_selection.empty()) {
3973 _editor->selection->add (to_be_added_to_selection);
3976 if (!to_be_removed_from_selection.empty()) {
3977 _editor->selection->remove (to_be_removed_from_selection);
3983 case SelectionStartTrim:
3985 start = _editor->selection->time[_editor->clicked_selection].start;
3986 end = _editor->selection->time[_editor->clicked_selection].end;
3988 if (pending_position > end) {
3991 start = pending_position;
3995 case SelectionEndTrim:
3997 start = _editor->selection->time[_editor->clicked_selection].start;
3998 end = _editor->selection->time[_editor->clicked_selection].end;
4000 if (pending_position < start) {
4003 end = pending_position;
4010 start = _editor->selection->time[_editor->clicked_selection].start;
4011 end = _editor->selection->time[_editor->clicked_selection].end;
4013 length = end - start;
4014 distance = pending_position - start;
4015 start = pending_position;
4016 _editor->snap_to (start);
4018 end = start + length;
4022 case SelectionExtend:
4027 switch (_operation) {
4029 if (_time_selection_at_start) {
4030 _editor->selection->move_time (distance);
4034 _editor->selection->replace (_editor->clicked_selection, start, end);
4038 if (_operation == SelectionMove) {
4039 show_verbose_cursor_time(start);
4041 show_verbose_cursor_time(pending_position);
4046 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4048 Session* s = _editor->session();
4050 if (movement_occurred) {
4051 motion (event, false);
4052 /* XXX this is not object-oriented programming at all. ick */
4053 if (_editor->selection->time.consolidate()) {
4054 _editor->selection->TimeChanged ();
4057 /* XXX what if its a music time selection? */
4059 if ( s->get_play_range() && s->transport_rolling() ) {
4060 s->request_play_range (&_editor->selection->time, true);
4062 if (Config->get_always_play_range() && !s->transport_rolling()) {
4063 s->request_locate (_editor->get_selection().time.start());
4069 /* just a click, no pointer movement.
4072 if (_operation == SelectionExtend) {
4073 if (_time_selection_at_start) {
4074 framepos_t pos = adjusted_current_frame (event, false);
4075 framepos_t start = min (pos, start_at_start);
4076 framepos_t end = max (pos, end_at_start);
4077 _editor->selection->set (start, end);
4080 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4081 if (_editor->clicked_selection) {
4082 _editor->selection->remove (_editor->clicked_selection);
4085 if (!_editor->clicked_selection) {
4086 _editor->selection->clear_time();
4091 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4092 _editor->selection->set (_editor->clicked_axisview);
4095 if (s && s->get_play_range () && s->transport_rolling()) {
4096 s->request_stop (false, false);
4101 _editor->stop_canvas_autoscroll ();
4102 _editor->clicked_selection = 0;
4106 SelectionDrag::aborted (bool)
4111 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4116 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4118 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4119 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4120 physical_screen_height (_editor->get_window())));
4121 _drag_rect->hide ();
4123 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4124 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4128 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4130 if (_editor->session() == 0) {
4134 Gdk::Cursor* cursor = 0;
4136 if (!_editor->temp_location) {
4137 _editor->temp_location = new Location (*_editor->session());
4140 switch (_operation) {
4141 case CreateRangeMarker:
4142 case CreateTransportMarker:
4143 case CreateCDMarker:
4145 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4150 cursor = _editor->cursors()->selector;
4154 Drag::start_grab (event, cursor);
4156 show_verbose_cursor_time (adjusted_current_frame (event));
4160 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4162 framepos_t start = 0;
4164 ArdourCanvas::Rectangle *crect;
4166 switch (_operation) {
4167 case CreateRangeMarker:
4168 crect = _editor->range_bar_drag_rect;
4170 case CreateTransportMarker:
4171 crect = _editor->transport_bar_drag_rect;
4173 case CreateCDMarker:
4174 crect = _editor->cd_marker_bar_drag_rect;
4177 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4182 framepos_t const pf = adjusted_current_frame (event);
4184 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4185 framepos_t grab = grab_frame ();
4186 _editor->snap_to (grab);
4188 if (pf < grab_frame()) {
4196 /* first drag: Either add to the selection
4197 or create a new selection.
4202 _editor->temp_location->set (start, end);
4206 update_item (_editor->temp_location);
4208 //_drag_rect->raise_to_top();
4214 _editor->temp_location->set (start, end);
4216 double x1 = _editor->sample_to_pixel (start);
4217 double x2 = _editor->sample_to_pixel (end);
4221 update_item (_editor->temp_location);
4224 show_verbose_cursor_time (pf);
4229 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4231 Location * newloc = 0;
4235 if (movement_occurred) {
4236 motion (event, false);
4239 switch (_operation) {
4240 case CreateRangeMarker:
4241 case CreateCDMarker:
4243 _editor->begin_reversible_command (_("new range marker"));
4244 XMLNode &before = _editor->session()->locations()->get_state();
4245 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4246 if (_operation == CreateCDMarker) {
4247 flags = Location::IsRangeMarker | Location::IsCDMarker;
4248 _editor->cd_marker_bar_drag_rect->hide();
4251 flags = Location::IsRangeMarker;
4252 _editor->range_bar_drag_rect->hide();
4254 newloc = new Location (
4255 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4258 _editor->session()->locations()->add (newloc, true);
4259 XMLNode &after = _editor->session()->locations()->get_state();
4260 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4261 _editor->commit_reversible_command ();
4265 case CreateTransportMarker:
4266 // popup menu to pick loop or punch
4267 _editor->new_transport_marker_context_menu (&event->button, _item);
4273 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4275 if (_operation == CreateTransportMarker) {
4277 /* didn't drag, so just locate */
4279 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4281 } else if (_operation == CreateCDMarker) {
4283 /* didn't drag, but mark is already created so do
4286 } else { /* operation == CreateRangeMarker */
4292 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4294 if (end == max_framepos) {
4295 end = _editor->session()->current_end_frame ();
4298 if (start == max_framepos) {
4299 start = _editor->session()->current_start_frame ();
4302 switch (_editor->mouse_mode) {
4304 /* find the two markers on either side and then make the selection from it */
4305 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4309 /* find the two markers on either side of the click and make the range out of it */
4310 _editor->selection->set (start, end);
4319 _editor->stop_canvas_autoscroll ();
4323 RangeMarkerBarDrag::aborted (bool)
4329 RangeMarkerBarDrag::update_item (Location* location)
4331 double const x1 = _editor->sample_to_pixel (location->start());
4332 double const x2 = _editor->sample_to_pixel (location->end());
4334 _drag_rect->set_x0 (x1);
4335 _drag_rect->set_x1 (x2);
4338 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4342 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4346 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4348 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4349 Drag::start_grab (event, _editor->cursors()->zoom_out);
4352 Drag::start_grab (event, _editor->cursors()->zoom_in);
4356 show_verbose_cursor_time (adjusted_current_frame (event));
4360 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4365 framepos_t const pf = adjusted_current_frame (event);
4367 framepos_t grab = grab_frame ();
4368 _editor->snap_to_with_modifier (grab, event);
4370 /* base start and end on initial click position */
4382 _editor->zoom_rect->show();
4383 _editor->zoom_rect->raise_to_top();
4386 _editor->reposition_zoom_rect(start, end);
4388 show_verbose_cursor_time (pf);
4393 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4395 if (movement_occurred) {
4396 motion (event, false);
4398 if (grab_frame() < last_pointer_frame()) {
4399 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4401 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4404 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4405 _editor->tav_zoom_step (_zoom_out);
4407 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4411 _editor->zoom_rect->hide();
4415 MouseZoomDrag::aborted (bool)
4417 _editor->zoom_rect->hide ();
4420 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4422 , _cumulative_dx (0)
4423 , _cumulative_dy (0)
4425 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4427 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4429 _region = &_primary->region_view ();
4430 _note_height = _region->midi_stream_view()->note_height ();
4434 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4436 Drag::start_grab (event);
4438 if (!(_was_selected = _primary->selected())) {
4440 /* tertiary-click means extend selection - we'll do that on button release,
4441 so don't add it here, because otherwise we make it hard to figure
4442 out the "extend-to" range.
4445 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4448 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4451 _region->note_selected (_primary, true);
4453 _region->unique_select (_primary);
4459 /** @return Current total drag x change in frames */
4461 NoteDrag::total_dx () const
4464 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4466 /* primary note time */
4467 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4469 /* new time of the primary note in session frames */
4470 frameoffset_t st = n + dx;
4472 framepos_t const rp = _region->region()->position ();
4474 /* prevent the note being dragged earlier than the region's position */
4477 /* snap and return corresponding delta */
4478 return _region->snap_frame_to_frame (st - rp) + rp - n;
4481 /** @return Current total drag y change in note number */
4483 NoteDrag::total_dy () const
4485 MidiStreamView* msv = _region->midi_stream_view ();
4486 double const y = _region->midi_view()->y_position ();
4487 /* new current note */
4488 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4490 n = max (msv->lowest_note(), n);
4491 n = min (msv->highest_note(), n);
4492 /* and work out delta */
4493 return n - msv->y_to_note (grab_y() - y);
4497 NoteDrag::motion (GdkEvent *, bool)
4499 /* Total change in x and y since the start of the drag */
4500 frameoffset_t const dx = total_dx ();
4501 int8_t const dy = total_dy ();
4503 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4504 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4505 double const tdy = -dy * _note_height - _cumulative_dy;
4508 _cumulative_dx += tdx;
4509 _cumulative_dy += tdy;
4511 int8_t note_delta = total_dy();
4513 _region->move_selection (tdx, tdy, note_delta);
4515 /* the new note value may be the same as the old one, but we
4516 * don't know what that means because the selection may have
4517 * involved more than one note and we might be doing something
4518 * odd with them. so show the note value anyway, always.
4522 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4524 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4525 (int) floor ((double)new_note));
4527 show_verbose_cursor_text (buf);
4532 NoteDrag::finished (GdkEvent* ev, bool moved)
4535 /* no motion - select note */
4537 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4538 _editor->current_mouse_mode() == Editing::MouseDraw) {
4540 if (_was_selected) {
4541 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4543 _region->note_deselected (_primary);
4546 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4547 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4549 if (!extend && !add && _region->selection_size() > 1) {
4550 _region->unique_select (_primary);
4551 } else if (extend) {
4552 _region->note_selected (_primary, true, true);
4554 /* it was added during button press */
4559 _region->note_dropped (_primary, total_dx(), total_dy());
4564 NoteDrag::aborted (bool)
4569 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4570 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4571 : Drag (editor, atv->base_item ())
4573 , _nothing_to_drag (false)
4575 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4576 y_origin = atv->y_position();
4577 setup (atv->lines ());
4580 /** Make an AutomationRangeDrag for region gain lines */
4581 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4582 : Drag (editor, rv->get_canvas_group ())
4584 , _nothing_to_drag (false)
4586 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4588 list<boost::shared_ptr<AutomationLine> > lines;
4589 lines.push_back (rv->get_gain_line ());
4590 y_origin = rv->get_time_axis_view().y_position();
4594 /** @param lines AutomationLines to drag.
4595 * @param offset Offset from the session start to the points in the AutomationLines.
4598 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4600 /* find the lines that overlap the ranges being dragged */
4601 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4602 while (i != lines.end ()) {
4603 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4606 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4608 /* check this range against all the AudioRanges that we are using */
4609 list<AudioRange>::const_iterator k = _ranges.begin ();
4610 while (k != _ranges.end()) {
4611 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4617 /* add it to our list if it overlaps at all */
4618 if (k != _ranges.end()) {
4623 _lines.push_back (n);
4629 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4633 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4635 return 1.0 - ((global_y - y_origin) / line->height());
4639 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4641 Drag::start_grab (event, cursor);
4643 /* Get line states before we start changing things */
4644 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4645 i->state = &i->line->get_state ();
4646 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4649 if (_ranges.empty()) {
4651 /* No selected time ranges: drag all points */
4652 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4653 uint32_t const N = i->line->npoints ();
4654 for (uint32_t j = 0; j < N; ++j) {
4655 i->points.push_back (i->line->nth (j));
4661 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4663 framecnt_t const half = (i->start + i->end) / 2;
4665 /* find the line that this audio range starts in */
4666 list<Line>::iterator j = _lines.begin();
4667 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4671 if (j != _lines.end()) {
4672 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4674 /* j is the line that this audio range starts in; fade into it;
4675 64 samples length plucked out of thin air.
4678 framepos_t a = i->start + 64;
4683 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4684 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4686 the_list->add (p, the_list->eval (p));
4687 the_list->add (q, the_list->eval (q));
4690 /* same thing for the end */
4693 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4697 if (j != _lines.end()) {
4698 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4700 /* j is the line that this audio range starts in; fade out of it;
4701 64 samples length plucked out of thin air.
4704 framepos_t b = i->end - 64;
4709 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4710 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4712 the_list->add (p, the_list->eval (p));
4713 the_list->add (q, the_list->eval (q));
4717 _nothing_to_drag = true;
4719 /* Find all the points that should be dragged and put them in the relevant
4720 points lists in the Line structs.
4723 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4725 uint32_t const N = i->line->npoints ();
4726 for (uint32_t j = 0; j < N; ++j) {
4728 /* here's a control point on this line */
4729 ControlPoint* p = i->line->nth (j);
4730 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4732 /* see if it's inside a range */
4733 list<AudioRange>::const_iterator k = _ranges.begin ();
4734 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4738 if (k != _ranges.end()) {
4739 /* dragging this point */
4740 _nothing_to_drag = false;
4741 i->points.push_back (p);
4747 if (_nothing_to_drag) {
4751 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4752 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4757 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4759 if (_nothing_to_drag) {
4763 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4764 float const f = y_fraction (l->line, _drags->current_pointer_y());
4765 /* we are ignoring x position for this drag, so we can just pass in anything */
4767 l->line->drag_motion (0, f, true, false, ignored);
4768 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4773 AutomationRangeDrag::finished (GdkEvent* event, bool)
4775 if (_nothing_to_drag) {
4779 motion (event, false);
4780 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4781 i->line->end_drag (false, 0);
4784 _editor->session()->commit_reversible_command ();
4788 AutomationRangeDrag::aborted (bool)
4790 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4795 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4798 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4799 layer = v->region()->layer ();
4800 initial_y = v->get_canvas_group()->position().y;
4801 initial_playlist = v->region()->playlist ();
4802 initial_position = v->region()->position ();
4803 initial_end = v->region()->position () + v->region()->length ();
4806 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4807 : Drag (e, i->canvas_item ())
4810 , _cumulative_dx (0)
4812 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4813 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4818 PatchChangeDrag::motion (GdkEvent* ev, bool)
4820 framepos_t f = adjusted_current_frame (ev);
4821 boost::shared_ptr<Region> r = _region_view->region ();
4822 f = max (f, r->position ());
4823 f = min (f, r->last_frame ());
4825 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4826 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4827 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4828 _cumulative_dx = dxu;
4832 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4834 if (!movement_occurred) {
4838 boost::shared_ptr<Region> r (_region_view->region ());
4839 framepos_t f = adjusted_current_frame (ev);
4840 f = max (f, r->position ());
4841 f = min (f, r->last_frame ());
4843 _region_view->move_patch_change (
4845 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4850 PatchChangeDrag::aborted (bool)
4852 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4856 PatchChangeDrag::setup_pointer_frame_offset ()
4858 boost::shared_ptr<Region> region = _region_view->region ();
4859 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4862 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4863 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4870 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4872 framepos_t const p = _region_view->region()->position ();
4873 double const y = _region_view->midi_view()->y_position ();
4875 x1 = max ((framepos_t) 0, x1 - p);
4876 x2 = max ((framepos_t) 0, x2 - p);
4877 y1 = max (0.0, y1 - y);
4878 y2 = max (0.0, y2 - y);
4880 _region_view->update_drag_selection (
4881 _editor->sample_to_pixel (x1),
4882 _editor->sample_to_pixel (x2),
4885 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4890 MidiRubberbandSelectDrag::deselect_things ()
4895 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4896 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4899 _vertical_only = true;
4903 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4905 double const y = _region_view->midi_view()->y_position ();
4907 y1 = max (0.0, y1 - y);
4908 y2 = max (0.0, y2 - y);
4910 _region_view->update_vertical_drag_selection (
4913 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4918 MidiVerticalSelectDrag::deselect_things ()
4923 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4924 : RubberbandSelectDrag (e, i)
4930 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4932 if (drag_in_progress) {
4933 /* We just want to select things at the end of the drag, not during it */
4937 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4939 _editor->begin_reversible_command (_("rubberband selection"));
4940 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4941 _editor->commit_reversible_command ();
4945 EditorRubberbandSelectDrag::deselect_things ()
4947 if (!getenv("ARDOUR_SAE")) {
4948 _editor->selection->clear_tracks();
4950 _editor->selection->clear_regions();
4951 _editor->selection->clear_points ();
4952 _editor->selection->clear_lines ();
4955 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4963 NoteCreateDrag::~NoteCreateDrag ()
4969 NoteCreateDrag::grid_frames (framepos_t t) const
4972 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4977 return _region_view->region_beats_to_region_frames (grid_beats);
4981 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4983 Drag::start_grab (event, cursor);
4985 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4987 framepos_t pf = _drags->current_pointer_frame ();
4988 framecnt_t const g = grid_frames (pf);
4990 /* Hack so that we always snap to the note that we are over, instead of snapping
4991 to the next one if we're more than halfway through the one we're over.
4993 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4997 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4999 MidiStreamView* sv = _region_view->midi_stream_view ();
5000 double const x = _editor->sample_to_pixel (_note[0]);
5001 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5003 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5004 _drag_rect->set_outline_all ();
5005 _drag_rect->set_outline_color (0xffffff99);
5006 _drag_rect->set_fill_color (0xffffff66);
5010 NoteCreateDrag::motion (GdkEvent* event, bool)
5012 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5013 double const x = _editor->sample_to_pixel (_note[1]);
5014 if (_note[1] > _note[0]) {
5015 _drag_rect->set_x1 (x);
5017 _drag_rect->set_x0 (x);
5022 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5024 if (!had_movement) {
5028 framepos_t const start = min (_note[0], _note[1]);
5029 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5031 framecnt_t const g = grid_frames (start);
5032 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5034 if (_editor->snap_mode() == SnapNormal && length < g) {
5035 length = g - one_tick;
5038 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5040 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5044 NoteCreateDrag::y_to_region (double y) const
5047 _region_view->get_canvas_group()->canvas_to_item (x, y);
5052 NoteCreateDrag::aborted (bool)
5057 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5062 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5066 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5068 Drag::start_grab (event, cursor);
5072 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5078 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5081 distance = _drags->current_pointer_x() - grab_x();
5082 len = ar->fade_in()->back()->when;
5084 distance = grab_x() - _drags->current_pointer_x();
5085 len = ar->fade_out()->back()->when;
5088 /* how long should it be ? */
5090 new_length = len + _editor->pixel_to_sample (distance);
5092 /* now check with the region that this is legal */
5094 new_length = ar->verify_xfade_bounds (new_length, start);
5097 arv->reset_fade_in_shape_width (ar, new_length);
5099 arv->reset_fade_out_shape_width (ar, new_length);
5104 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5110 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5113 distance = _drags->current_pointer_x() - grab_x();
5114 len = ar->fade_in()->back()->when;
5116 distance = grab_x() - _drags->current_pointer_x();
5117 len = ar->fade_out()->back()->when;
5120 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5122 _editor->begin_reversible_command ("xfade trim");
5123 ar->playlist()->clear_owned_changes ();
5126 ar->set_fade_in_length (new_length);
5128 ar->set_fade_out_length (new_length);
5131 /* Adjusting the xfade may affect other regions in the playlist, so we need
5132 to get undo Commands from the whole playlist rather than just the
5136 vector<Command*> cmds;
5137 ar->playlist()->rdiff (cmds);
5138 _editor->session()->add_commands (cmds);
5139 _editor->commit_reversible_command ();
5144 CrossfadeEdgeDrag::aborted (bool)
5147 arv->redraw_start_xfade ();
5149 arv->redraw_end_xfade ();