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);
710 /* If we have moved tracks, we'll fudge the layer delta so that the
711 region gets moved back onto layer 0 on its new track; this avoids
712 confusion when dragging regions from non-zero layers onto different
715 double this_delta_layer = delta_layer;
716 if (delta_time_axis_view != 0) {
717 this_delta_layer = - i->layer;
722 int track_index = i->time_axis_view + delta_time_axis_view;
724 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
728 /* The TimeAxisView that this region is now over */
729 TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
731 /* Ensure it is moved from stacked -> expanded if appropriate */
732 if (current_tv->view()->layer_display() == Stacked) {
733 current_tv->view()->set_layer_display (Expanded);
736 /* We're only allowed to go -ve in layer on Expanded views */
737 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
738 this_delta_layer = - i->layer;
742 rv->set_height (current_tv->view()->child_height ());
744 /* Update show/hidden status as the region view may have come from a hidden track,
745 or have moved to one.
747 if (current_tv->hidden ()) {
748 rv->get_canvas_group()->hide ();
750 rv->get_canvas_group()->show ();
753 /* Update the DraggingView */
754 i->time_axis_view += delta_time_axis_view;
755 i->layer += this_delta_layer;
758 _editor->mouse_brush_insert_region (rv, pending_region_position);
762 /* Get the y coordinate of the top of the track that this region is now over */
763 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
765 /* And adjust for the layer that it should be on */
766 StreamView* cv = current_tv->view ();
767 switch (cv->layer_display ()) {
771 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
774 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
778 /* need to get the parent of the regionview
779 * canvas group and get its position in
780 * equivalent coordinate space as the trackview
781 * we are now dragging over.
784 /* Now move the region view */
785 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0, 0)).y);
789 /* Only move the region into the empty dropzone at the bottom if the pointer
793 if (_drags->current_pointer_y() >= _editor->get_trackview_group()->item_to_canvas (Duple (0,0)).y) {
796 TimeAxisView* last = _time_axis_views.back();
797 track_origin = last->canvas_display()->item_to_canvas (track_origin);
798 track_origin.y += last->effective_height();
799 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0,0)).y);
800 i->time_axis_view = -1;
804 } /* foreach region */
806 _total_x_delta += x_delta;
808 if (x_delta != 0 && !_brushing) {
809 show_verbose_cursor_time (_last_frame_position);
812 _last_pointer_time_axis_view += delta_time_axis_view;
813 _last_pointer_layer += delta_layer;
817 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
819 if (_copy && first_move) {
821 /* duplicate the regionview(s) and region(s) */
823 list<DraggingView> new_regionviews;
825 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
827 RegionView* rv = i->view;
828 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
829 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
831 const boost::shared_ptr<const Region> original = rv->region();
832 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
833 region_copy->set_position (original->position());
837 boost::shared_ptr<AudioRegion> audioregion_copy
838 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
840 nrv = new AudioRegionView (*arv, audioregion_copy);
842 boost::shared_ptr<MidiRegion> midiregion_copy
843 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
844 nrv = new MidiRegionView (*mrv, midiregion_copy);
849 nrv->get_canvas_group()->show ();
850 new_regionviews.push_back (DraggingView (nrv, this));
852 /* swap _primary to the copy */
854 if (rv == _primary) {
858 /* ..and deselect the one we copied */
860 rv->set_selected (false);
863 if (!new_regionviews.empty()) {
865 /* reflect the fact that we are dragging the copies */
867 _views = new_regionviews;
869 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
873 RegionMotionDrag::motion (event, first_move);
877 RegionMotionDrag::finished (GdkEvent *, bool)
879 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
884 if ((*i)->view()->layer_display() == Expanded) {
885 (*i)->view()->set_layer_display (Stacked);
891 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
893 RegionMotionDrag::finished (ev, movement_occurred);
895 if (!movement_occurred) {
899 if (was_double_click() && !_views.empty()) {
900 DraggingView dv = _views.front();
901 dv.view->show_region_editor ();
908 /* reverse this here so that we have the correct logic to finalize
912 if (Config->get_edit_mode() == Lock) {
913 _x_constrained = !_x_constrained;
916 assert (!_views.empty ());
918 /* We might have hidden region views so that they weren't visible during the drag
919 (when they have been reparented). Now everything can be shown again, as region
920 views are back in their track parent groups.
922 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
923 i->view->get_canvas_group()->show ();
926 bool const changed_position = (_last_frame_position != _primary->region()->position());
927 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
928 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
948 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
952 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
954 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
959 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
960 list<boost::shared_ptr<AudioTrack> > audio_tracks;
961 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
962 return _editor->axis_view_from_route (audio_tracks.front());
964 ChanCount one_midi_port (DataType::MIDI, 1);
965 list<boost::shared_ptr<MidiTrack> > midi_tracks;
966 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
967 return _editor->axis_view_from_route (midi_tracks.front());
970 error << _("Could not create new track after region placed in the drop zone") << endmsg;
976 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
978 RegionSelection new_views;
979 PlaylistSet modified_playlists;
980 RouteTimeAxisView* new_time_axis_view = 0;
983 /* all changes were made during motion event handlers */
985 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
989 _editor->commit_reversible_command ();
993 if (_x_constrained) {
994 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
996 _editor->begin_reversible_command (Operations::region_copy);
999 /* insert the regions into their new playlists */
1000 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1002 RouteTimeAxisView* dest_rtv = 0;
1004 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1010 if (changed_position && !_x_constrained) {
1011 where = i->view->region()->position() - drag_delta;
1013 where = i->view->region()->position();
1016 if (i->time_axis_view < 0) {
1017 if (!new_time_axis_view) {
1018 new_time_axis_view = create_destination_time_axis (i->view->region());
1020 dest_rtv = new_time_axis_view;
1022 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1025 if (dest_rtv != 0) {
1026 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1027 if (new_view != 0) {
1028 new_views.push_back (new_view);
1032 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1033 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1036 list<DraggingView>::const_iterator next = i;
1042 /* If we've created new regions either by copying or moving
1043 to a new track, we want to replace the old selection with the new ones
1046 if (new_views.size() > 0) {
1047 _editor->selection->set (new_views);
1050 /* write commands for the accumulated diffs for all our modified playlists */
1051 add_stateful_diff_commands_for_playlists (modified_playlists);
1053 _editor->commit_reversible_command ();
1057 RegionMoveDrag::finished_no_copy (
1058 bool const changed_position,
1059 bool const changed_tracks,
1060 framecnt_t const drag_delta
1063 RegionSelection new_views;
1064 PlaylistSet modified_playlists;
1065 PlaylistSet frozen_playlists;
1066 set<RouteTimeAxisView*> views_to_update;
1067 RouteTimeAxisView* new_time_axis_view = 0;
1070 /* all changes were made during motion event handlers */
1071 _editor->commit_reversible_command ();
1075 if (_x_constrained) {
1076 _editor->begin_reversible_command (_("fixed time region drag"));
1078 _editor->begin_reversible_command (Operations::region_drag);
1081 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1083 RegionView* rv = i->view;
1084 RouteTimeAxisView* dest_rtv = 0;
1086 if (rv->region()->locked() || rv->region()->video_locked()) {
1091 if (i->time_axis_view < 0) {
1092 if (!new_time_axis_view) {
1093 new_time_axis_view = create_destination_time_axis (rv->region());
1095 dest_rtv = new_time_axis_view;
1097 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1102 double const dest_layer = i->layer;
1104 views_to_update.insert (dest_rtv);
1108 if (changed_position && !_x_constrained) {
1109 where = rv->region()->position() - drag_delta;
1111 where = rv->region()->position();
1114 if (changed_tracks) {
1116 /* insert into new playlist */
1118 RegionView* new_view = insert_region_into_playlist (
1119 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1122 if (new_view == 0) {
1127 new_views.push_back (new_view);
1129 /* remove from old playlist */
1131 /* the region that used to be in the old playlist is not
1132 moved to the new one - we use a copy of it. as a result,
1133 any existing editor for the region should no longer be
1136 rv->hide_region_editor();
1137 rv->fake_set_opaque (false);
1139 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1143 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1145 /* this movement may result in a crossfade being modified, or a layering change,
1146 so we need to get undo data from the playlist as well as the region.
1149 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1151 playlist->clear_changes ();
1154 rv->region()->clear_changes ();
1157 motion on the same track. plonk the previously reparented region
1158 back to its original canvas group (its streamview).
1159 No need to do anything for copies as they are fake regions which will be deleted.
1162 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1163 rv->get_canvas_group()->set_y_position (i->initial_y);
1166 /* just change the model */
1167 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1168 playlist->set_layer (rv->region(), dest_layer);
1171 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1173 r = frozen_playlists.insert (playlist);
1176 playlist->freeze ();
1179 rv->region()->set_position (where);
1181 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1184 if (changed_tracks) {
1186 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1187 was selected in all of them, then removing it from a playlist will have removed all
1188 trace of it from _views (i.e. there were N regions selected, we removed 1,
1189 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1190 corresponding regionview, and _views is now empty).
1192 This could have invalidated any and all iterators into _views.
1194 The heuristic we use here is: if the region selection is empty, break out of the loop
1195 here. if the region selection is not empty, then restart the loop because we know that
1196 we must have removed at least the region(view) we've just been working on as well as any
1197 that we processed on previous iterations.
1199 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1200 we can just iterate.
1204 if (_views.empty()) {
1215 /* If we've created new regions either by copying or moving
1216 to a new track, we want to replace the old selection with the new ones
1219 if (new_views.size() > 0) {
1220 _editor->selection->set (new_views);
1223 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1227 /* write commands for the accumulated diffs for all our modified playlists */
1228 add_stateful_diff_commands_for_playlists (modified_playlists);
1230 _editor->commit_reversible_command ();
1232 /* We have futzed with the layering of canvas items on our streamviews.
1233 If any region changed layer, this will have resulted in the stream
1234 views being asked to set up their region views, and all will be well.
1235 If not, we might now have badly-ordered region views. Ask the StreamViews
1236 involved to sort themselves out, just in case.
1239 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1240 (*i)->view()->playlist_layered ((*i)->track ());
1244 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1245 * @param region Region to remove.
1246 * @param playlist playlist To remove from.
1247 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1248 * that clear_changes () is only called once per playlist.
1251 RegionMoveDrag::remove_region_from_playlist (
1252 boost::shared_ptr<Region> region,
1253 boost::shared_ptr<Playlist> playlist,
1254 PlaylistSet& modified_playlists
1257 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1260 playlist->clear_changes ();
1263 playlist->remove_region (region);
1267 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1268 * clearing the playlist's diff history first if necessary.
1269 * @param region Region to insert.
1270 * @param dest_rtv Destination RouteTimeAxisView.
1271 * @param dest_layer Destination layer.
1272 * @param where Destination position.
1273 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1274 * that clear_changes () is only called once per playlist.
1275 * @return New RegionView, or 0 if no insert was performed.
1278 RegionMoveDrag::insert_region_into_playlist (
1279 boost::shared_ptr<Region> region,
1280 RouteTimeAxisView* dest_rtv,
1283 PlaylistSet& modified_playlists
1286 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1287 if (!dest_playlist) {
1291 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1292 _new_region_view = 0;
1293 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1295 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1296 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1298 dest_playlist->clear_changes ();
1301 dest_playlist->add_region (region, where);
1303 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1304 dest_playlist->set_layer (region, dest_layer);
1309 assert (_new_region_view);
1311 return _new_region_view;
1315 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1317 _new_region_view = rv;
1321 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1323 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1324 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1326 _editor->session()->add_command (c);
1335 RegionMoveDrag::aborted (bool movement_occurred)
1339 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1346 RegionMotionDrag::aborted (movement_occurred);
1351 RegionMotionDrag::aborted (bool)
1353 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1355 StreamView* sview = (*i)->view();
1358 if (sview->layer_display() == Expanded) {
1359 sview->set_layer_display (Stacked);
1364 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1365 RegionView* rv = i->view;
1366 TimeAxisView* tv = &(rv->get_time_axis_view ());
1367 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1369 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1370 rv->get_canvas_group()->set_y_position (0);
1372 rv->fake_set_opaque (false);
1373 rv->move (-_total_x_delta, 0);
1374 rv->set_height (rtv->view()->child_height ());
1378 /** @param b true to brush, otherwise false.
1379 * @param c true to make copies of the regions being moved, otherwise false.
1381 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1382 : RegionMotionDrag (e, i, p, v, b),
1385 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1388 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1389 if (rtv && rtv->is_track()) {
1390 speed = rtv->track()->speed ();
1393 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1397 RegionMoveDrag::setup_pointer_frame_offset ()
1399 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1402 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1403 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1405 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1407 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1408 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1410 _primary = v->view()->create_region_view (r, false, false);
1412 _primary->get_canvas_group()->show ();
1413 _primary->set_position (pos, 0);
1414 _views.push_back (DraggingView (_primary, this));
1416 _last_frame_position = pos;
1418 _item = _primary->get_canvas_group ();
1422 RegionInsertDrag::finished (GdkEvent *, bool)
1424 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1426 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1427 _primary->get_canvas_group()->set_y_position (0);
1429 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1431 _editor->begin_reversible_command (Operations::insert_region);
1432 playlist->clear_changes ();
1433 playlist->add_region (_primary->region (), _last_frame_position);
1434 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1435 _editor->commit_reversible_command ();
1443 RegionInsertDrag::aborted (bool)
1450 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1451 : RegionMoveDrag (e, i, p, v, false, false)
1453 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1456 struct RegionSelectionByPosition {
1457 bool operator() (RegionView*a, RegionView* b) {
1458 return a->region()->position () < b->region()->position();
1463 RegionSpliceDrag::motion (GdkEvent* event, bool)
1465 /* Which trackview is this ? */
1467 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1468 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1470 /* The region motion is only processed if the pointer is over
1474 if (!tv || !tv->is_track()) {
1475 /* To make sure we hide the verbose canvas cursor when the mouse is
1476 not held over and audiotrack.
1478 _editor->verbose_cursor()->hide ();
1484 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1490 RegionSelection copy (_editor->selection->regions);
1492 RegionSelectionByPosition cmp;
1495 framepos_t const pf = adjusted_current_frame (event);
1497 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1499 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1505 boost::shared_ptr<Playlist> playlist;
1507 if ((playlist = atv->playlist()) == 0) {
1511 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1516 if (pf < (*i)->region()->last_frame() + 1) {
1520 if (pf > (*i)->region()->first_frame()) {
1526 playlist->shuffle ((*i)->region(), dir);
1531 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1533 RegionMoveDrag::finished (event, movement_occurred);
1537 RegionSpliceDrag::aborted (bool)
1542 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1544 _view (dynamic_cast<MidiTimeAxisView*> (v))
1546 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1552 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1555 _region = add_midi_region (_view);
1556 _view->playlist()->freeze ();
1559 framepos_t const f = adjusted_current_frame (event);
1560 if (f < grab_frame()) {
1561 _region->set_position (f);
1564 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1565 so that if this region is duplicated, its duplicate starts on
1566 a snap point rather than 1 frame after a snap point. Otherwise things get
1567 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1568 place snapped notes at the start of the region.
1571 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1572 _region->set_length (len < 1 ? 1 : len);
1578 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1580 if (!movement_occurred) {
1581 add_midi_region (_view);
1583 _view->playlist()->thaw ();
1588 RegionCreateDrag::aborted (bool)
1591 _view->playlist()->thaw ();
1597 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1601 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1605 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1607 Gdk::Cursor* cursor;
1608 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1610 float x_fraction = cnote->mouse_x_fraction ();
1612 if (x_fraction > 0.0 && x_fraction < 0.25) {
1613 cursor = _editor->cursors()->left_side_trim;
1615 cursor = _editor->cursors()->right_side_trim;
1618 Drag::start_grab (event, cursor);
1620 region = &cnote->region_view();
1622 double const region_start = region->get_position_pixels();
1623 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1625 if (grab_x() <= middle_point) {
1626 cursor = _editor->cursors()->left_side_trim;
1629 cursor = _editor->cursors()->right_side_trim;
1635 if (event->motion.state & Keyboard::PrimaryModifier) {
1641 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1643 if (ms.size() > 1) {
1644 /* has to be relative, may make no sense otherwise */
1648 /* select this note; if it is already selected, preserve the existing selection,
1649 otherwise make this note the only one selected.
1651 region->note_selected (cnote, cnote->selected ());
1653 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1654 MidiRegionSelection::iterator next;
1657 (*r)->begin_resizing (at_front);
1663 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1665 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1666 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1667 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1669 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1674 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1676 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1677 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1678 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1680 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1685 NoteResizeDrag::aborted (bool)
1687 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1688 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1689 (*r)->abort_resizing ();
1693 AVDraggingView::AVDraggingView (RegionView* v)
1696 initial_position = v->region()->position ();
1699 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1702 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1705 TrackViewList empty;
1707 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1708 std::list<RegionView*> views = rs.by_layer();
1710 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1711 RegionView* rv = (*i);
1712 if (!rv->region()->video_locked()) {
1715 _views.push_back (AVDraggingView (rv));
1720 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1722 Drag::start_grab (event);
1723 if (_editor->session() == 0) {
1727 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1728 _max_backwards_drag = (
1729 ARDOUR_UI::instance()->video_timeline->get_duration()
1730 + ARDOUR_UI::instance()->video_timeline->get_offset()
1731 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1734 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1735 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1736 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1739 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1742 Timecode::Time timecode;
1743 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1744 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);
1745 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1746 _editor->verbose_cursor()->show ();
1750 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1752 if (_editor->session() == 0) {
1755 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1759 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1760 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1762 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1763 dt = - _max_backwards_drag;
1766 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1767 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1769 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1770 RegionView* rv = i->view;
1771 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1774 rv->fake_set_opaque (true);
1775 rv->region()->clear_changes ();
1776 rv->region()->suspend_property_changes();
1778 rv->region()->set_position(i->initial_position + dt);
1779 rv->region_changed(ARDOUR::Properties::position);
1782 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1783 Timecode::Time timecode;
1784 Timecode::Time timediff;
1786 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1787 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1788 snprintf (buf, sizeof (buf),
1789 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1790 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1791 , _("Video Start:"),
1792 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1794 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1796 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1797 _editor->verbose_cursor()->show ();
1801 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1803 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1807 if (!movement_occurred || ! _editor->session()) {
1811 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1813 _editor->begin_reversible_command (_("Move Video"));
1815 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1816 ARDOUR_UI::instance()->video_timeline->save_undo();
1817 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1818 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1820 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1821 i->view->drag_end();
1822 i->view->fake_set_opaque (false);
1823 i->view->region()->resume_property_changes ();
1825 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1828 _editor->session()->maybe_update_session_range(
1829 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1830 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1834 _editor->commit_reversible_command ();
1838 VideoTimeLineDrag::aborted (bool)
1840 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1843 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1844 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1846 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1847 i->view->region()->resume_property_changes ();
1848 i->view->region()->set_position(i->initial_position);
1852 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1853 : RegionDrag (e, i, p, v)
1854 , _preserve_fade_anchor (preserve_fade_anchor)
1855 , _jump_position_when_done (false)
1857 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1861 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1864 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1865 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1867 if (tv && tv->is_track()) {
1868 speed = tv->track()->speed();
1871 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1872 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1873 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1875 framepos_t const pf = adjusted_current_frame (event);
1877 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1878 /* Move the contents of the region around without changing the region bounds */
1879 _operation = ContentsTrim;
1880 Drag::start_grab (event, _editor->cursors()->trimmer);
1882 /* These will get overridden for a point trim.*/
1883 if (pf < (region_start + region_length/2)) {
1884 /* closer to front */
1885 _operation = StartTrim;
1886 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1889 _operation = EndTrim;
1890 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1894 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1895 _jump_position_when_done = true;
1898 switch (_operation) {
1900 show_verbose_cursor_time (region_start);
1901 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1902 i->view->trim_front_starting ();
1906 show_verbose_cursor_time (region_end);
1909 show_verbose_cursor_time (pf);
1913 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1914 i->view->region()->suspend_property_changes ();
1919 TrimDrag::motion (GdkEvent* event, bool first_move)
1921 RegionView* rv = _primary;
1924 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1925 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1926 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1927 frameoffset_t frame_delta = 0;
1929 if (tv && tv->is_track()) {
1930 speed = tv->track()->speed();
1933 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1939 switch (_operation) {
1941 trim_type = "Region start trim";
1944 trim_type = "Region end trim";
1947 trim_type = "Region content trim";
1954 _editor->begin_reversible_command (trim_type);
1956 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1957 RegionView* rv = i->view;
1958 rv->fake_set_opaque (false);
1959 rv->enable_display (false);
1960 rv->region()->playlist()->clear_owned_changes ();
1962 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1965 arv->temporarily_hide_envelope ();
1969 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1970 insert_result = _editor->motion_frozen_playlists.insert (pl);
1972 if (insert_result.second) {
1978 bool non_overlap_trim = false;
1980 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1981 non_overlap_trim = true;
1984 /* contstrain trim to fade length */
1985 if (_preserve_fade_anchor) {
1986 switch (_operation) {
1988 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1989 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1991 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1992 if (ar->locked()) continue;
1993 framecnt_t len = ar->fade_in()->back()->when;
1994 if (len < dt) dt = min(dt, len);
1998 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1999 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2001 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2002 if (ar->locked()) continue;
2003 framecnt_t len = ar->fade_out()->back()->when;
2004 if (len < -dt) dt = max(dt, -len);
2013 switch (_operation) {
2015 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2016 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2017 if (changed && _preserve_fade_anchor) {
2018 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2020 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2021 framecnt_t len = ar->fade_in()->back()->when;
2022 framecnt_t diff = ar->first_frame() - i->initial_position;
2023 framepos_t new_length = len - diff;
2024 i->anchored_fade_length = min (ar->length(), new_length);
2025 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2026 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2033 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2034 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2035 if (changed && _preserve_fade_anchor) {
2036 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2038 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2039 framecnt_t len = ar->fade_out()->back()->when;
2040 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2041 framepos_t new_length = len + diff;
2042 i->anchored_fade_length = min (ar->length(), new_length);
2043 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2044 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2052 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2054 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2055 i->view->move_contents (frame_delta);
2061 switch (_operation) {
2063 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2066 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2069 // show_verbose_cursor_time (frame_delta);
2076 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2078 if (movement_occurred) {
2079 motion (event, false);
2081 if (_operation == StartTrim) {
2082 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2084 /* This must happen before the region's StatefulDiffCommand is created, as it may
2085 `correct' (ahem) the region's _start from being negative to being zero. It
2086 needs to be zero in the undo record.
2088 i->view->trim_front_ending ();
2090 if (_preserve_fade_anchor) {
2091 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2093 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2094 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2095 ar->set_fade_in_length(i->anchored_fade_length);
2096 ar->set_fade_in_active(true);
2099 if (_jump_position_when_done) {
2100 i->view->region()->set_position (i->initial_position);
2103 } else if (_operation == EndTrim) {
2104 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2105 if (_preserve_fade_anchor) {
2106 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2108 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2109 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2110 ar->set_fade_out_length(i->anchored_fade_length);
2111 ar->set_fade_out_active(true);
2114 if (_jump_position_when_done) {
2115 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2120 if (!_views.empty()) {
2121 if (_operation == StartTrim) {
2122 _editor->maybe_locate_with_edit_preroll(
2123 _views.begin()->view->region()->position());
2125 if (_operation == EndTrim) {
2126 _editor->maybe_locate_with_edit_preroll(
2127 _views.begin()->view->region()->position() +
2128 _views.begin()->view->region()->length());
2132 if (!_editor->selection->selected (_primary)) {
2133 _primary->thaw_after_trim ();
2136 set<boost::shared_ptr<Playlist> > diffed_playlists;
2138 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2139 i->view->thaw_after_trim ();
2140 i->view->enable_display (true);
2141 i->view->fake_set_opaque (true);
2143 /* Trimming one region may affect others on the playlist, so we need
2144 to get undo Commands from the whole playlist rather than just the
2145 region. Use diffed_playlists to make sure we don't diff a given
2146 playlist more than once.
2148 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2149 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2150 vector<Command*> cmds;
2152 _editor->session()->add_commands (cmds);
2153 diffed_playlists.insert (p);
2158 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2162 _editor->motion_frozen_playlists.clear ();
2163 _editor->commit_reversible_command();
2166 /* no mouse movement */
2167 _editor->point_trim (event, adjusted_current_frame (event));
2170 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2171 if (_operation == StartTrim) {
2172 i->view->trim_front_ending ();
2175 i->view->region()->resume_property_changes ();
2180 TrimDrag::aborted (bool movement_occurred)
2182 /* Our motion method is changing model state, so use the Undo system
2183 to cancel. Perhaps not ideal, as this will leave an Undo point
2184 behind which may be slightly odd from the user's point of view.
2189 if (movement_occurred) {
2193 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2194 i->view->region()->resume_property_changes ();
2199 TrimDrag::setup_pointer_frame_offset ()
2201 list<DraggingView>::iterator i = _views.begin ();
2202 while (i != _views.end() && i->view != _primary) {
2206 if (i == _views.end()) {
2210 switch (_operation) {
2212 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2215 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2222 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2226 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2227 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2232 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2234 Drag::start_grab (event, cursor);
2235 show_verbose_cursor_time (adjusted_current_frame(event));
2239 MeterMarkerDrag::setup_pointer_frame_offset ()
2241 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2245 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2247 if (!_marker->meter().movable()) {
2253 // create a dummy marker for visual representation of moving the
2254 // section, because whether its a copy or not, we're going to
2255 // leave or lose the original marker (leave if its a copy; lose if its
2256 // not, because we'll remove it from the map).
2258 MeterSection section (_marker->meter());
2260 if (!section.movable()) {
2265 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2267 _marker = new MeterMarker (
2269 *_editor->meter_group,
2270 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2272 *new MeterSection (_marker->meter())
2275 /* use the new marker for the grab */
2276 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2279 TempoMap& map (_editor->session()->tempo_map());
2280 /* get current state */
2281 before_state = &map.get_state();
2282 /* remove the section while we drag it */
2283 map.remove_meter (section, true);
2287 framepos_t const pf = adjusted_current_frame (event);
2288 _marker->set_position (pf);
2289 show_verbose_cursor_time (pf);
2293 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2295 if (!movement_occurred) {
2296 if (was_double_click()) {
2297 _editor->edit_meter_marker (*_marker);
2302 if (!_marker->meter().movable()) {
2306 motion (event, false);
2308 Timecode::BBT_Time when;
2310 TempoMap& map (_editor->session()->tempo_map());
2311 map.bbt_time (last_pointer_frame(), when);
2313 if (_copy == true) {
2314 _editor->begin_reversible_command (_("copy meter mark"));
2315 XMLNode &before = map.get_state();
2316 map.add_meter (_marker->meter(), when);
2317 XMLNode &after = map.get_state();
2318 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2319 _editor->commit_reversible_command ();
2322 _editor->begin_reversible_command (_("move meter mark"));
2324 /* we removed it before, so add it back now */
2326 map.add_meter (_marker->meter(), when);
2327 XMLNode &after = map.get_state();
2328 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2329 _editor->commit_reversible_command ();
2332 // delete the dummy marker we used for visual representation while moving.
2333 // a new visual marker will show up automatically.
2338 MeterMarkerDrag::aborted (bool moved)
2340 _marker->set_position (_marker->meter().frame ());
2343 TempoMap& map (_editor->session()->tempo_map());
2344 /* we removed it before, so add it back now */
2345 map.add_meter (_marker->meter(), _marker->meter().frame());
2346 // delete the dummy marker we used for visual representation while moving.
2347 // a new visual marker will show up automatically.
2352 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2356 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2358 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2363 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2365 Drag::start_grab (event, cursor);
2366 show_verbose_cursor_time (adjusted_current_frame (event));
2370 TempoMarkerDrag::setup_pointer_frame_offset ()
2372 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2376 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2378 if (!_marker->tempo().movable()) {
2384 // create a dummy marker for visual representation of moving the
2385 // section, because whether its a copy or not, we're going to
2386 // leave or lose the original marker (leave if its a copy; lose if its
2387 // not, because we'll remove it from the map).
2389 // create a dummy marker for visual representation of moving the copy.
2390 // The actual copying is not done before we reach the finish callback.
2393 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2395 TempoSection section (_marker->tempo());
2397 _marker = new TempoMarker (
2399 *_editor->tempo_group,
2400 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2402 *new TempoSection (_marker->tempo())
2405 /* use the new marker for the grab */
2406 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2409 TempoMap& map (_editor->session()->tempo_map());
2410 /* get current state */
2411 before_state = &map.get_state();
2412 /* remove the section while we drag it */
2413 map.remove_tempo (section, true);
2417 framepos_t const pf = adjusted_current_frame (event);
2418 _marker->set_position (pf);
2419 show_verbose_cursor_time (pf);
2423 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2425 if (!movement_occurred) {
2426 if (was_double_click()) {
2427 _editor->edit_tempo_marker (*_marker);
2432 if (!_marker->tempo().movable()) {
2436 motion (event, false);
2438 TempoMap& map (_editor->session()->tempo_map());
2439 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2440 Timecode::BBT_Time when;
2442 map.bbt_time (beat_time, when);
2444 if (_copy == true) {
2445 _editor->begin_reversible_command (_("copy tempo mark"));
2446 XMLNode &before = map.get_state();
2447 map.add_tempo (_marker->tempo(), when);
2448 XMLNode &after = map.get_state();
2449 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2450 _editor->commit_reversible_command ();
2453 _editor->begin_reversible_command (_("move tempo mark"));
2454 /* we removed it before, so add it back now */
2455 map.add_tempo (_marker->tempo(), when);
2456 XMLNode &after = map.get_state();
2457 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2458 _editor->commit_reversible_command ();
2461 // delete the dummy marker we used for visual representation while moving.
2462 // a new visual marker will show up automatically.
2467 TempoMarkerDrag::aborted (bool moved)
2469 _marker->set_position (_marker->tempo().frame());
2471 TempoMap& map (_editor->session()->tempo_map());
2472 /* we removed it before, so add it back now */
2473 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2474 // delete the dummy marker we used for visual representation while moving.
2475 // a new visual marker will show up automatically.
2480 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2481 : Drag (e, &c.track_canvas_item())
2485 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2488 /** Do all the things we do when dragging the playhead to make it look as though
2489 * we have located, without actually doing the locate (because that would cause
2490 * the diskstream buffers to be refilled, which is too slow).
2493 CursorDrag::fake_locate (framepos_t t)
2495 _editor->playhead_cursor->set_position (t);
2497 Session* s = _editor->session ();
2498 if (s->timecode_transmission_suspended ()) {
2499 framepos_t const f = _editor->playhead_cursor->current_frame ();
2500 /* This is asynchronous so it will be sent "now"
2502 s->send_mmc_locate (f);
2503 /* These are synchronous and will be sent during the next
2506 s->queue_full_time_code ();
2507 s->queue_song_position_pointer ();
2510 show_verbose_cursor_time (t);
2511 _editor->UpdateAllTransportClocks (t);
2515 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2517 Drag::start_grab (event, c);
2519 _grab_zoom = _editor->samples_per_pixel;
2521 framepos_t where = _editor->canvas_event_sample (event);
2523 _editor->snap_to_with_modifier (where, event);
2525 _editor->_dragging_playhead = true;
2527 Session* s = _editor->session ();
2529 /* grab the track canvas item as well */
2531 _cursor.track_canvas_item().grab();
2534 if (_was_rolling && _stop) {
2538 if (s->is_auditioning()) {
2539 s->cancel_audition ();
2543 if (AudioEngine::instance()->connected()) {
2545 /* do this only if we're the engine is connected
2546 * because otherwise this request will never be
2547 * serviced and we'll busy wait forever. likewise,
2548 * notice if we are disconnected while waiting for the
2549 * request to be serviced.
2552 s->request_suspend_timecode_transmission ();
2553 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2554 /* twiddle our thumbs */
2559 fake_locate (where);
2563 CursorDrag::motion (GdkEvent* event, bool)
2565 framepos_t const adjusted_frame = adjusted_current_frame (event);
2566 if (adjusted_frame != last_pointer_frame()) {
2567 fake_locate (adjusted_frame);
2572 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2574 _editor->_dragging_playhead = false;
2576 _cursor.track_canvas_item().ungrab();
2578 if (!movement_occurred && _stop) {
2582 motion (event, false);
2584 Session* s = _editor->session ();
2586 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2587 _editor->_pending_locate_request = true;
2588 s->request_resume_timecode_transmission ();
2593 CursorDrag::aborted (bool)
2595 _cursor.track_canvas_item().ungrab();
2597 if (_editor->_dragging_playhead) {
2598 _editor->session()->request_resume_timecode_transmission ();
2599 _editor->_dragging_playhead = false;
2602 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2605 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2606 : RegionDrag (e, i, p, v)
2608 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2612 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2614 Drag::start_grab (event, cursor);
2616 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2617 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2619 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2623 FadeInDrag::setup_pointer_frame_offset ()
2625 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2626 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2627 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2631 FadeInDrag::motion (GdkEvent* event, bool)
2633 framecnt_t fade_length;
2635 framepos_t const pos = adjusted_current_frame (event);
2637 boost::shared_ptr<Region> region = _primary->region ();
2639 if (pos < (region->position() + 64)) {
2640 fade_length = 64; // this should be a minimum defined somewhere
2641 } else if (pos > region->last_frame()) {
2642 fade_length = region->length();
2644 fade_length = pos - region->position();
2647 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2649 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2655 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2658 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2662 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2664 if (!movement_occurred) {
2668 framecnt_t fade_length;
2670 framepos_t const pos = adjusted_current_frame (event);
2672 boost::shared_ptr<Region> region = _primary->region ();
2674 if (pos < (region->position() + 64)) {
2675 fade_length = 64; // this should be a minimum defined somewhere
2676 } else if (pos > region->last_frame()) {
2677 fade_length = region->length();
2679 fade_length = pos - region->position();
2682 _editor->begin_reversible_command (_("change fade in length"));
2684 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2686 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2692 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2693 XMLNode &before = alist->get_state();
2695 tmp->audio_region()->set_fade_in_length (fade_length);
2696 tmp->audio_region()->set_fade_in_active (true);
2698 XMLNode &after = alist->get_state();
2699 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2702 _editor->commit_reversible_command ();
2706 FadeInDrag::aborted (bool)
2708 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2709 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2715 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2719 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2720 : RegionDrag (e, i, p, v)
2722 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2726 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2728 Drag::start_grab (event, cursor);
2730 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2731 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2733 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2737 FadeOutDrag::setup_pointer_frame_offset ()
2739 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2740 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2741 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2745 FadeOutDrag::motion (GdkEvent* event, bool)
2747 framecnt_t fade_length;
2749 framepos_t const pos = adjusted_current_frame (event);
2751 boost::shared_ptr<Region> region = _primary->region ();
2753 if (pos > (region->last_frame() - 64)) {
2754 fade_length = 64; // this should really be a minimum fade defined somewhere
2756 else if (pos < region->position()) {
2757 fade_length = region->length();
2760 fade_length = region->last_frame() - pos;
2763 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2765 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2771 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2774 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2778 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2780 if (!movement_occurred) {
2784 framecnt_t fade_length;
2786 framepos_t const pos = adjusted_current_frame (event);
2788 boost::shared_ptr<Region> region = _primary->region ();
2790 if (pos > (region->last_frame() - 64)) {
2791 fade_length = 64; // this should really be a minimum fade defined somewhere
2793 else if (pos < region->position()) {
2794 fade_length = region->length();
2797 fade_length = region->last_frame() - pos;
2800 _editor->begin_reversible_command (_("change fade out length"));
2802 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2804 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2810 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2811 XMLNode &before = alist->get_state();
2813 tmp->audio_region()->set_fade_out_length (fade_length);
2814 tmp->audio_region()->set_fade_out_active (true);
2816 XMLNode &after = alist->get_state();
2817 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2820 _editor->commit_reversible_command ();
2824 FadeOutDrag::aborted (bool)
2826 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2827 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2833 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2837 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2840 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2842 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2845 _points.push_back (ArdourCanvas::Duple (0, 0));
2846 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2849 MarkerDrag::~MarkerDrag ()
2851 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2856 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2858 location = new Location (*l);
2859 markers.push_back (m);
2864 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2866 Drag::start_grab (event, cursor);
2870 Location *location = _editor->find_location_from_marker (_marker, is_start);
2871 _editor->_dragging_edit_point = true;
2873 update_item (location);
2875 // _drag_line->show();
2876 // _line->raise_to_top();
2879 show_verbose_cursor_time (location->start());
2881 show_verbose_cursor_time (location->end());
2884 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2887 case Selection::Toggle:
2888 /* we toggle on the button release */
2890 case Selection::Set:
2891 if (!_editor->selection->selected (_marker)) {
2892 _editor->selection->set (_marker);
2895 case Selection::Extend:
2897 Locations::LocationList ll;
2898 list<Marker*> to_add;
2900 _editor->selection->markers.range (s, e);
2901 s = min (_marker->position(), s);
2902 e = max (_marker->position(), e);
2905 if (e < max_framepos) {
2908 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2909 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2910 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2913 to_add.push_back (lm->start);
2916 to_add.push_back (lm->end);
2920 if (!to_add.empty()) {
2921 _editor->selection->add (to_add);
2925 case Selection::Add:
2926 _editor->selection->add (_marker);
2930 /* Set up copies for us to manipulate during the drag
2933 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2935 Location* l = _editor->find_location_from_marker (*i, is_start);
2942 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2944 /* range: check that the other end of the range isn't
2947 CopiedLocationInfo::iterator x;
2948 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2949 if (*(*x).location == *l) {
2953 if (x == _copied_locations.end()) {
2954 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2956 (*x).markers.push_back (*i);
2957 (*x).move_both = true;
2965 MarkerDrag::setup_pointer_frame_offset ()
2968 Location *location = _editor->find_location_from_marker (_marker, is_start);
2969 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2973 MarkerDrag::motion (GdkEvent* event, bool)
2975 framecnt_t f_delta = 0;
2977 bool move_both = false;
2978 Location *real_location;
2979 Location *copy_location = 0;
2981 framepos_t const newframe = adjusted_current_frame (event);
2982 framepos_t next = newframe;
2984 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2988 CopiedLocationInfo::iterator x;
2990 /* find the marker we're dragging, and compute the delta */
2992 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2994 copy_location = (*x).location;
2996 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2998 /* this marker is represented by this
2999 * CopiedLocationMarkerInfo
3002 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3007 if (real_location->is_mark()) {
3008 f_delta = newframe - copy_location->start();
3012 switch (_marker->type()) {
3013 case Marker::SessionStart:
3014 case Marker::RangeStart:
3015 case Marker::LoopStart:
3016 case Marker::PunchIn:
3017 f_delta = newframe - copy_location->start();
3020 case Marker::SessionEnd:
3021 case Marker::RangeEnd:
3022 case Marker::LoopEnd:
3023 case Marker::PunchOut:
3024 f_delta = newframe - copy_location->end();
3027 /* what kind of marker is this ? */
3036 if (x == _copied_locations.end()) {
3037 /* hmm, impossible - we didn't find the dragged marker */
3041 /* now move them all */
3043 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3045 copy_location = x->location;
3047 /* call this to find out if its the start or end */
3049 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3053 if (real_location->locked()) {
3057 if (copy_location->is_mark()) {
3061 copy_location->set_start (copy_location->start() + f_delta);
3065 framepos_t new_start = copy_location->start() + f_delta;
3066 framepos_t new_end = copy_location->end() + f_delta;
3068 if (is_start) { // start-of-range marker
3070 if (move_both || (*x).move_both) {
3071 copy_location->set_start (new_start);
3072 copy_location->set_end (new_end);
3073 } else if (new_start < copy_location->end()) {
3074 copy_location->set_start (new_start);
3075 } else if (newframe > 0) {
3076 _editor->snap_to (next, 1, true);
3077 copy_location->set_end (next);
3078 copy_location->set_start (newframe);
3081 } else { // end marker
3083 if (move_both || (*x).move_both) {
3084 copy_location->set_end (new_end);
3085 copy_location->set_start (new_start);
3086 } else if (new_end > copy_location->start()) {
3087 copy_location->set_end (new_end);
3088 } else if (newframe > 0) {
3089 _editor->snap_to (next, -1, true);
3090 copy_location->set_start (next);
3091 copy_location->set_end (newframe);
3096 update_item (copy_location);
3098 /* now lookup the actual GUI items used to display this
3099 * location and move them to wherever the copy of the location
3100 * is now. This means that the logic in ARDOUR::Location is
3101 * still enforced, even though we are not (yet) modifying
3102 * the real Location itself.
3105 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3108 lm->set_position (copy_location->start(), copy_location->end());
3113 assert (!_copied_locations.empty());
3115 show_verbose_cursor_time (newframe);
3119 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3121 if (!movement_occurred) {
3123 if (was_double_click()) {
3124 _editor->rename_marker (_marker);
3128 /* just a click, do nothing but finish
3129 off the selection process
3132 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3135 case Selection::Set:
3136 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3137 _editor->selection->set (_marker);
3141 case Selection::Toggle:
3142 /* we toggle on the button release, click only */
3143 _editor->selection->toggle (_marker);
3146 case Selection::Extend:
3147 case Selection::Add:
3154 _editor->_dragging_edit_point = false;
3156 _editor->begin_reversible_command ( _("move marker") );
3157 XMLNode &before = _editor->session()->locations()->get_state();
3159 MarkerSelection::iterator i;
3160 CopiedLocationInfo::iterator x;
3163 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3164 x != _copied_locations.end() && i != _editor->selection->markers.end();
3167 Location * location = _editor->find_location_from_marker (*i, is_start);
3171 if (location->locked()) {
3175 if (location->is_mark()) {
3176 location->set_start (((*x).location)->start());
3178 location->set (((*x).location)->start(), ((*x).location)->end());
3183 XMLNode &after = _editor->session()->locations()->get_state();
3184 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3185 _editor->commit_reversible_command ();
3189 MarkerDrag::aborted (bool)
3195 MarkerDrag::update_item (Location*)
3200 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3202 _cumulative_x_drag (0),
3203 _cumulative_y_drag (0)
3205 if (_zero_gain_fraction < 0.0) {
3206 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3209 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3211 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3217 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3219 Drag::start_grab (event, _editor->cursors()->fader);
3221 // start the grab at the center of the control point so
3222 // the point doesn't 'jump' to the mouse after the first drag
3223 _fixed_grab_x = _point->get_x();
3224 _fixed_grab_y = _point->get_y();
3226 float const fraction = 1 - (_point->get_y() / _point->line().height());
3228 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3230 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3231 event->button.x + 10, event->button.y + 10);
3233 _editor->verbose_cursor()->show ();
3235 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3237 if (!_point->can_slide ()) {
3238 _x_constrained = true;
3243 ControlPointDrag::motion (GdkEvent* event, bool)
3245 double dx = _drags->current_pointer_x() - last_pointer_x();
3246 double dy = _drags->current_pointer_y() - last_pointer_y();
3248 if (event->button.state & Keyboard::SecondaryModifier) {
3253 /* coordinate in pixels relative to the start of the region (for region-based automation)
3254 or track (for track-based automation) */
3255 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3256 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3258 // calculate zero crossing point. back off by .01 to stay on the
3259 // positive side of zero
3260 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3262 // make sure we hit zero when passing through
3263 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3267 if (_x_constrained) {
3270 if (_y_constrained) {
3274 _cumulative_x_drag = cx - _fixed_grab_x;
3275 _cumulative_y_drag = cy - _fixed_grab_y;
3279 cy = min ((double) _point->line().height(), cy);
3281 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3283 if (!_x_constrained) {
3284 _editor->snap_to_with_modifier (cx_frames, event);
3287 cx_frames = min (cx_frames, _point->line().maximum_time());
3289 float const fraction = 1.0 - (cy / _point->line().height());
3291 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3293 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3297 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3299 if (!movement_occurred) {
3303 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3304 _editor->reset_point_selection ();
3308 motion (event, false);
3311 _point->line().end_drag (_pushing, _final_index);
3312 _editor->session()->commit_reversible_command ();
3316 ControlPointDrag::aborted (bool)
3318 _point->line().reset ();
3322 ControlPointDrag::active (Editing::MouseMode m)
3324 if (m == Editing::MouseGain) {
3325 /* always active in mouse gain */
3329 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3330 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3333 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3336 _cumulative_y_drag (0)
3338 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3342 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3344 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3347 _item = &_line->grab_item ();
3349 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3350 origin, and ditto for y.
3353 double cx = event->button.x;
3354 double cy = event->button.y;
3356 _line->parent_group().canvas_to_item (cx, cy);
3358 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3363 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3364 /* no adjacent points */
3368 Drag::start_grab (event, _editor->cursors()->fader);
3370 /* store grab start in parent frame */
3375 double fraction = 1.0 - (cy / _line->height());
3377 _line->start_drag_line (before, after, fraction);
3379 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3380 event->button.x + 10, event->button.y + 10);
3382 _editor->verbose_cursor()->show ();
3386 LineDrag::motion (GdkEvent* event, bool)
3388 double dy = _drags->current_pointer_y() - last_pointer_y();
3390 if (event->button.state & Keyboard::SecondaryModifier) {
3394 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3396 _cumulative_y_drag = cy - _fixed_grab_y;
3399 cy = min ((double) _line->height(), cy);
3401 double const fraction = 1.0 - (cy / _line->height());
3404 /* we are ignoring x position for this drag, so we can just pass in anything */
3405 _line->drag_motion (0, fraction, true, false, ignored);
3407 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3411 LineDrag::finished (GdkEvent* event, bool movement_occured)
3413 if (movement_occured) {
3414 motion (event, false);
3415 _line->end_drag (false, 0);
3417 /* add a new control point on the line */
3419 AutomationTimeAxisView* atv;
3421 _line->end_drag (false, 0);
3423 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3424 framepos_t where = _editor->window_event_sample (event, 0, 0);
3425 atv->add_automation_event (event, where, event->button.y, false);
3429 _editor->session()->commit_reversible_command ();
3433 LineDrag::aborted (bool)
3438 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3441 _cumulative_x_drag (0)
3443 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3447 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3449 Drag::start_grab (event);
3451 _line = reinterpret_cast<Line*> (_item);
3454 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3456 double cx = event->button.x;
3457 double cy = event->button.y;
3459 _item->parent()->canvas_to_item (cx, cy);
3461 /* store grab start in parent frame */
3462 _region_view_grab_x = cx;
3464 _before = *(float*) _item->get_data ("position");
3466 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3468 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3472 FeatureLineDrag::motion (GdkEvent*, bool)
3474 double dx = _drags->current_pointer_x() - last_pointer_x();
3476 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3478 _cumulative_x_drag += dx;
3480 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3489 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3491 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3493 float *pos = new float;
3496 _line->set_data ("position", pos);
3502 FeatureLineDrag::finished (GdkEvent*, bool)
3504 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3505 _arv->update_transient(_before, _before);
3509 FeatureLineDrag::aborted (bool)
3514 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3516 , _vertical_only (false)
3518 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3522 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3524 Drag::start_grab (event);
3525 show_verbose_cursor_time (adjusted_current_frame (event));
3529 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3536 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3538 framepos_t grab = grab_frame ();
3539 if (Config->get_rubberbanding_snaps_to_grid ()) {
3540 _editor->snap_to_with_modifier (grab, event);
3543 /* base start and end on initial click position */
3553 if (_drags->current_pointer_y() < grab_y()) {
3554 y1 = _drags->current_pointer_y();
3557 y2 = _drags->current_pointer_y();
3562 if (start != end || y1 != y2) {
3564 double x1 = _editor->sample_to_pixel (start);
3565 double x2 = _editor->sample_to_pixel (end);
3566 const double min_dimension = 2.0;
3568 _editor->rubberband_rect->set_x0 (x1);
3569 if (_vertical_only) {
3570 /* fixed 10 pixel width */
3571 _editor->rubberband_rect->set_x1 (x1 + 10);
3574 x2 = min (x1 - min_dimension, x2);
3576 x2 = max (x1 + min_dimension, x2);
3578 _editor->rubberband_rect->set_x1 (x2);
3581 _editor->rubberband_rect->set_y0 (y1);
3583 y2 = min (y1 - min_dimension, y2);
3585 y2 = max (y1 + min_dimension, y2);
3588 _editor->rubberband_rect->set_y1 (y2);
3590 _editor->rubberband_rect->show();
3591 _editor->rubberband_rect->raise_to_top();
3593 show_verbose_cursor_time (pf);
3595 do_select_things (event, true);
3600 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3605 if (grab_frame() < last_pointer_frame()) {
3607 x2 = last_pointer_frame ();
3610 x1 = last_pointer_frame ();
3616 if (_drags->current_pointer_y() < grab_y()) {
3617 y1 = _drags->current_pointer_y();
3620 y2 = _drags->current_pointer_y();
3624 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3628 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3630 if (movement_occurred) {
3632 motion (event, false);
3633 do_select_things (event, false);
3639 bool do_deselect = true;
3640 MidiTimeAxisView* mtv;
3642 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3644 if (_editor->selection->empty()) {
3645 /* nothing selected */
3646 add_midi_region (mtv);
3647 do_deselect = false;
3651 /* do not deselect if Primary or Tertiary (toggle-select or
3652 * extend-select are pressed.
3655 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3656 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3663 _editor->rubberband_rect->hide();
3667 RubberbandSelectDrag::aborted (bool)
3669 _editor->rubberband_rect->hide ();
3672 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3673 : RegionDrag (e, i, p, v)
3675 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3679 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3681 Drag::start_grab (event, cursor);
3683 show_verbose_cursor_time (adjusted_current_frame (event));
3687 TimeFXDrag::motion (GdkEvent* event, bool)
3689 RegionView* rv = _primary;
3690 StreamView* cv = rv->get_time_axis_view().view ();
3692 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3693 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3694 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3696 framepos_t const pf = adjusted_current_frame (event);
3698 if (pf > rv->region()->position()) {
3699 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3702 show_verbose_cursor_time (pf);
3706 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3708 _primary->get_time_axis_view().hide_timestretch ();
3710 if (!movement_occurred) {
3714 if (last_pointer_frame() < _primary->region()->position()) {
3715 /* backwards drag of the left edge - not usable */
3719 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3721 float percentage = (double) newlen / (double) _primary->region()->length();
3723 #ifndef USE_RUBBERBAND
3724 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3725 if (_primary->region()->data_type() == DataType::AUDIO) {
3726 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3730 if (!_editor->get_selection().regions.empty()) {
3731 /* primary will already be included in the selection, and edit
3732 group shared editing will propagate selection across
3733 equivalent regions, so just use the current region
3737 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3738 error << _("An error occurred while executing time stretch operation") << endmsg;
3744 TimeFXDrag::aborted (bool)
3746 _primary->get_time_axis_view().hide_timestretch ();
3749 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3752 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3756 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3758 Drag::start_grab (event);
3762 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3764 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3768 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3770 if (movement_occurred && _editor->session()) {
3771 /* make sure we stop */
3772 _editor->session()->request_transport_speed (0.0);
3777 ScrubDrag::aborted (bool)
3782 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3787 , _original_pointer_time_axis (-1)
3788 , _last_pointer_time_axis (-1)
3789 , _time_selection_at_start (!_editor->get_selection().time.empty())
3791 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3793 if (_time_selection_at_start) {
3794 start_at_start = _editor->get_selection().time.start();
3795 end_at_start = _editor->get_selection().time.end_frame();
3800 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3802 if (_editor->session() == 0) {
3806 Gdk::Cursor* cursor = 0;
3808 switch (_operation) {
3809 case CreateSelection:
3810 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3815 cursor = _editor->cursors()->selector;
3816 Drag::start_grab (event, cursor);
3819 case SelectionStartTrim:
3820 if (_editor->clicked_axisview) {
3821 _editor->clicked_axisview->order_selection_trims (_item, true);
3823 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3826 case SelectionEndTrim:
3827 if (_editor->clicked_axisview) {
3828 _editor->clicked_axisview->order_selection_trims (_item, false);
3830 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3834 Drag::start_grab (event, cursor);
3837 case SelectionExtend:
3838 Drag::start_grab (event, cursor);
3842 if (_operation == SelectionMove) {
3843 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3845 show_verbose_cursor_time (adjusted_current_frame (event));
3848 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3852 SelectionDrag::setup_pointer_frame_offset ()
3854 switch (_operation) {
3855 case CreateSelection:
3856 _pointer_frame_offset = 0;
3859 case SelectionStartTrim:
3861 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3864 case SelectionEndTrim:
3865 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3868 case SelectionExtend:
3874 SelectionDrag::motion (GdkEvent* event, bool first_move)
3876 framepos_t start = 0;
3878 framecnt_t length = 0;
3879 framecnt_t distance = 0;
3881 framepos_t const pending_position = adjusted_current_frame (event);
3883 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3887 switch (_operation) {
3888 case CreateSelection:
3890 framepos_t grab = grab_frame ();
3893 grab = adjusted_current_frame (event, false);
3894 if (grab < pending_position) {
3895 _editor->snap_to (grab, -1);
3897 _editor->snap_to (grab, 1);
3901 if (pending_position < grab) {
3902 start = pending_position;
3905 end = pending_position;
3909 /* first drag: Either add to the selection
3910 or create a new selection
3917 /* adding to the selection */
3918 _editor->set_selected_track_as_side_effect (Selection::Add);
3919 _editor->clicked_selection = _editor->selection->add (start, end);
3926 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3927 _editor->set_selected_track_as_side_effect (Selection::Set);
3930 _editor->clicked_selection = _editor->selection->set (start, end);
3934 /* select all tracks within the rectangle that we've marked out so far */
3935 TrackViewList to_be_added_to_selection;
3936 TrackViewList to_be_removed_from_selection;
3937 TrackViewList& all_tracks (_editor->track_views);
3939 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3941 if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
3942 if (!(*i)->get_selected()) {
3943 to_be_added_to_selection.push_back (*i);
3946 if ((*i)->get_selected()) {
3947 to_be_removed_from_selection.push_back (*i);
3952 if (!to_be_added_to_selection.empty()) {
3953 _editor->selection->add (to_be_added_to_selection);
3956 if (!to_be_removed_from_selection.empty()) {
3957 _editor->selection->remove (to_be_removed_from_selection);
3962 case SelectionStartTrim:
3964 start = _editor->selection->time[_editor->clicked_selection].start;
3965 end = _editor->selection->time[_editor->clicked_selection].end;
3967 if (pending_position > end) {
3970 start = pending_position;
3974 case SelectionEndTrim:
3976 start = _editor->selection->time[_editor->clicked_selection].start;
3977 end = _editor->selection->time[_editor->clicked_selection].end;
3979 if (pending_position < start) {
3982 end = pending_position;
3989 start = _editor->selection->time[_editor->clicked_selection].start;
3990 end = _editor->selection->time[_editor->clicked_selection].end;
3992 length = end - start;
3993 distance = pending_position - start;
3994 start = pending_position;
3995 _editor->snap_to (start);
3997 end = start + length;
4001 case SelectionExtend:
4006 switch (_operation) {
4008 if (_time_selection_at_start) {
4009 _editor->selection->move_time (distance);
4013 _editor->selection->replace (_editor->clicked_selection, start, end);
4017 if (_operation == SelectionMove) {
4018 show_verbose_cursor_time(start);
4020 show_verbose_cursor_time(pending_position);
4025 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4027 Session* s = _editor->session();
4029 if (movement_occurred) {
4030 motion (event, false);
4031 /* XXX this is not object-oriented programming at all. ick */
4032 if (_editor->selection->time.consolidate()) {
4033 _editor->selection->TimeChanged ();
4036 /* XXX what if its a music time selection? */
4038 if ( s->get_play_range() && s->transport_rolling() ) {
4039 s->request_play_range (&_editor->selection->time, true);
4041 if (Config->get_always_play_range() && !s->transport_rolling()) {
4042 s->request_locate (_editor->get_selection().time.start());
4048 /* just a click, no pointer movement.
4051 if (_operation == SelectionExtend) {
4052 if (_time_selection_at_start) {
4053 framepos_t pos = adjusted_current_frame (event, false);
4054 framepos_t start = min (pos, start_at_start);
4055 framepos_t end = max (pos, end_at_start);
4056 _editor->selection->set (start, end);
4059 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4060 if (_editor->clicked_selection) {
4061 _editor->selection->remove (_editor->clicked_selection);
4064 if (!_editor->clicked_selection) {
4065 _editor->selection->clear_time();
4070 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4071 _editor->selection->set (_editor->clicked_axisview);
4074 if (s && s->get_play_range () && s->transport_rolling()) {
4075 s->request_stop (false, false);
4080 _editor->stop_canvas_autoscroll ();
4081 _editor->clicked_selection = 0;
4085 SelectionDrag::aborted (bool)
4090 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4095 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4097 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4098 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4099 physical_screen_height (_editor->get_window())));
4100 _drag_rect->hide ();
4102 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4103 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4107 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4109 if (_editor->session() == 0) {
4113 Gdk::Cursor* cursor = 0;
4115 if (!_editor->temp_location) {
4116 _editor->temp_location = new Location (*_editor->session());
4119 switch (_operation) {
4120 case CreateRangeMarker:
4121 case CreateTransportMarker:
4122 case CreateCDMarker:
4124 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4129 cursor = _editor->cursors()->selector;
4133 Drag::start_grab (event, cursor);
4135 show_verbose_cursor_time (adjusted_current_frame (event));
4139 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4141 framepos_t start = 0;
4143 ArdourCanvas::Rectangle *crect;
4145 switch (_operation) {
4146 case CreateRangeMarker:
4147 crect = _editor->range_bar_drag_rect;
4149 case CreateTransportMarker:
4150 crect = _editor->transport_bar_drag_rect;
4152 case CreateCDMarker:
4153 crect = _editor->cd_marker_bar_drag_rect;
4156 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4161 framepos_t const pf = adjusted_current_frame (event);
4163 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4164 framepos_t grab = grab_frame ();
4165 _editor->snap_to (grab);
4167 if (pf < grab_frame()) {
4175 /* first drag: Either add to the selection
4176 or create a new selection.
4181 _editor->temp_location->set (start, end);
4185 update_item (_editor->temp_location);
4187 //_drag_rect->raise_to_top();
4193 _editor->temp_location->set (start, end);
4195 double x1 = _editor->sample_to_pixel (start);
4196 double x2 = _editor->sample_to_pixel (end);
4200 update_item (_editor->temp_location);
4203 show_verbose_cursor_time (pf);
4208 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4210 Location * newloc = 0;
4214 if (movement_occurred) {
4215 motion (event, false);
4218 switch (_operation) {
4219 case CreateRangeMarker:
4220 case CreateCDMarker:
4222 _editor->begin_reversible_command (_("new range marker"));
4223 XMLNode &before = _editor->session()->locations()->get_state();
4224 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4225 if (_operation == CreateCDMarker) {
4226 flags = Location::IsRangeMarker | Location::IsCDMarker;
4227 _editor->cd_marker_bar_drag_rect->hide();
4230 flags = Location::IsRangeMarker;
4231 _editor->range_bar_drag_rect->hide();
4233 newloc = new Location (
4234 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4237 _editor->session()->locations()->add (newloc, true);
4238 XMLNode &after = _editor->session()->locations()->get_state();
4239 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4240 _editor->commit_reversible_command ();
4244 case CreateTransportMarker:
4245 // popup menu to pick loop or punch
4246 _editor->new_transport_marker_context_menu (&event->button, _item);
4252 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4254 if (_operation == CreateTransportMarker) {
4256 /* didn't drag, so just locate */
4258 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4260 } else if (_operation == CreateCDMarker) {
4262 /* didn't drag, but mark is already created so do
4265 } else { /* operation == CreateRangeMarker */
4271 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4273 if (end == max_framepos) {
4274 end = _editor->session()->current_end_frame ();
4277 if (start == max_framepos) {
4278 start = _editor->session()->current_start_frame ();
4281 switch (_editor->mouse_mode) {
4283 /* find the two markers on either side and then make the selection from it */
4284 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4288 /* find the two markers on either side of the click and make the range out of it */
4289 _editor->selection->set (start, end);
4298 _editor->stop_canvas_autoscroll ();
4302 RangeMarkerBarDrag::aborted (bool)
4308 RangeMarkerBarDrag::update_item (Location* location)
4310 double const x1 = _editor->sample_to_pixel (location->start());
4311 double const x2 = _editor->sample_to_pixel (location->end());
4313 _drag_rect->set_x0 (x1);
4314 _drag_rect->set_x1 (x2);
4317 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4321 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4325 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4327 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4328 Drag::start_grab (event, _editor->cursors()->zoom_out);
4331 Drag::start_grab (event, _editor->cursors()->zoom_in);
4335 show_verbose_cursor_time (adjusted_current_frame (event));
4339 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4344 framepos_t const pf = adjusted_current_frame (event);
4346 framepos_t grab = grab_frame ();
4347 _editor->snap_to_with_modifier (grab, event);
4349 /* base start and end on initial click position */
4361 _editor->zoom_rect->show();
4362 _editor->zoom_rect->raise_to_top();
4365 _editor->reposition_zoom_rect(start, end);
4367 show_verbose_cursor_time (pf);
4372 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4374 if (movement_occurred) {
4375 motion (event, false);
4377 if (grab_frame() < last_pointer_frame()) {
4378 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4380 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4383 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4384 _editor->tav_zoom_step (_zoom_out);
4386 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4390 _editor->zoom_rect->hide();
4394 MouseZoomDrag::aborted (bool)
4396 _editor->zoom_rect->hide ();
4399 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4401 , _cumulative_dx (0)
4402 , _cumulative_dy (0)
4404 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4406 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4408 _region = &_primary->region_view ();
4409 _note_height = _region->midi_stream_view()->note_height ();
4413 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4415 Drag::start_grab (event);
4417 if (!(_was_selected = _primary->selected())) {
4419 /* tertiary-click means extend selection - we'll do that on button release,
4420 so don't add it here, because otherwise we make it hard to figure
4421 out the "extend-to" range.
4424 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4427 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4430 _region->note_selected (_primary, true);
4432 _region->unique_select (_primary);
4438 /** @return Current total drag x change in frames */
4440 NoteDrag::total_dx () const
4443 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4445 /* primary note time */
4446 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4448 /* new time of the primary note in session frames */
4449 frameoffset_t st = n + dx;
4451 framepos_t const rp = _region->region()->position ();
4453 /* prevent the note being dragged earlier than the region's position */
4456 /* snap and return corresponding delta */
4457 return _region->snap_frame_to_frame (st - rp) + rp - n;
4460 /** @return Current total drag y change in note number */
4462 NoteDrag::total_dy () const
4464 MidiStreamView* msv = _region->midi_stream_view ();
4465 double const y = _region->midi_view()->y_position ();
4466 /* new current note */
4467 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4469 n = max (msv->lowest_note(), n);
4470 n = min (msv->highest_note(), n);
4471 /* and work out delta */
4472 return n - msv->y_to_note (grab_y() - y);
4476 NoteDrag::motion (GdkEvent *, bool)
4478 /* Total change in x and y since the start of the drag */
4479 frameoffset_t const dx = total_dx ();
4480 int8_t const dy = total_dy ();
4482 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4483 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4484 double const tdy = -dy * _note_height - _cumulative_dy;
4487 _cumulative_dx += tdx;
4488 _cumulative_dy += tdy;
4490 int8_t note_delta = total_dy();
4492 _region->move_selection (tdx, tdy, note_delta);
4494 /* the new note value may be the same as the old one, but we
4495 * don't know what that means because the selection may have
4496 * involved more than one note and we might be doing something
4497 * odd with them. so show the note value anyway, always.
4501 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4503 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4504 (int) floor ((double)new_note));
4506 show_verbose_cursor_text (buf);
4511 NoteDrag::finished (GdkEvent* ev, bool moved)
4514 /* no motion - select note */
4516 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4517 _editor->current_mouse_mode() == Editing::MouseDraw) {
4519 if (_was_selected) {
4520 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4522 _region->note_deselected (_primary);
4525 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4526 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4528 if (!extend && !add && _region->selection_size() > 1) {
4529 _region->unique_select (_primary);
4530 } else if (extend) {
4531 _region->note_selected (_primary, true, true);
4533 /* it was added during button press */
4538 _region->note_dropped (_primary, total_dx(), total_dy());
4543 NoteDrag::aborted (bool)
4548 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4549 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4550 : Drag (editor, atv->base_item ())
4552 , _nothing_to_drag (false)
4554 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4555 y_origin = atv->y_position();
4556 setup (atv->lines ());
4559 /** Make an AutomationRangeDrag for region gain lines */
4560 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4561 : Drag (editor, rv->get_canvas_group ())
4563 , _nothing_to_drag (false)
4565 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4567 list<boost::shared_ptr<AutomationLine> > lines;
4568 lines.push_back (rv->get_gain_line ());
4569 y_origin = rv->get_time_axis_view().y_position();
4573 /** @param lines AutomationLines to drag.
4574 * @param offset Offset from the session start to the points in the AutomationLines.
4577 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4579 /* find the lines that overlap the ranges being dragged */
4580 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4581 while (i != lines.end ()) {
4582 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4585 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4587 /* check this range against all the AudioRanges that we are using */
4588 list<AudioRange>::const_iterator k = _ranges.begin ();
4589 while (k != _ranges.end()) {
4590 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4596 /* add it to our list if it overlaps at all */
4597 if (k != _ranges.end()) {
4602 _lines.push_back (n);
4608 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4612 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4614 return 1.0 - ((global_y - y_origin) / line->height());
4618 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4620 Drag::start_grab (event, cursor);
4622 /* Get line states before we start changing things */
4623 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4624 i->state = &i->line->get_state ();
4625 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4628 if (_ranges.empty()) {
4630 /* No selected time ranges: drag all points */
4631 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4632 uint32_t const N = i->line->npoints ();
4633 for (uint32_t j = 0; j < N; ++j) {
4634 i->points.push_back (i->line->nth (j));
4640 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4642 framecnt_t const half = (i->start + i->end) / 2;
4644 /* find the line that this audio range starts in */
4645 list<Line>::iterator j = _lines.begin();
4646 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4650 if (j != _lines.end()) {
4651 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4653 /* j is the line that this audio range starts in; fade into it;
4654 64 samples length plucked out of thin air.
4657 framepos_t a = i->start + 64;
4662 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4663 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4665 the_list->add (p, the_list->eval (p));
4666 the_list->add (q, the_list->eval (q));
4669 /* same thing for the end */
4672 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4676 if (j != _lines.end()) {
4677 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4679 /* j is the line that this audio range starts in; fade out of it;
4680 64 samples length plucked out of thin air.
4683 framepos_t b = i->end - 64;
4688 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4689 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4691 the_list->add (p, the_list->eval (p));
4692 the_list->add (q, the_list->eval (q));
4696 _nothing_to_drag = true;
4698 /* Find all the points that should be dragged and put them in the relevant
4699 points lists in the Line structs.
4702 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4704 uint32_t const N = i->line->npoints ();
4705 for (uint32_t j = 0; j < N; ++j) {
4707 /* here's a control point on this line */
4708 ControlPoint* p = i->line->nth (j);
4709 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4711 /* see if it's inside a range */
4712 list<AudioRange>::const_iterator k = _ranges.begin ();
4713 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4717 if (k != _ranges.end()) {
4718 /* dragging this point */
4719 _nothing_to_drag = false;
4720 i->points.push_back (p);
4726 if (_nothing_to_drag) {
4730 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4731 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4736 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4738 if (_nothing_to_drag) {
4742 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4743 float const f = y_fraction (l->line, _drags->current_pointer_y());
4744 /* we are ignoring x position for this drag, so we can just pass in anything */
4746 l->line->drag_motion (0, f, true, false, ignored);
4747 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4752 AutomationRangeDrag::finished (GdkEvent* event, bool)
4754 if (_nothing_to_drag) {
4758 motion (event, false);
4759 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4760 i->line->end_drag (false, 0);
4763 _editor->session()->commit_reversible_command ();
4767 AutomationRangeDrag::aborted (bool)
4769 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4774 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4777 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4778 layer = v->region()->layer ();
4779 initial_y = v->get_canvas_group()->position().y;
4780 initial_playlist = v->region()->playlist ();
4781 initial_position = v->region()->position ();
4782 initial_end = v->region()->position () + v->region()->length ();
4785 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4786 : Drag (e, i->canvas_item ())
4789 , _cumulative_dx (0)
4791 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4792 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4797 PatchChangeDrag::motion (GdkEvent* ev, bool)
4799 framepos_t f = adjusted_current_frame (ev);
4800 boost::shared_ptr<Region> r = _region_view->region ();
4801 f = max (f, r->position ());
4802 f = min (f, r->last_frame ());
4804 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4805 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4806 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4807 _cumulative_dx = dxu;
4811 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4813 if (!movement_occurred) {
4817 boost::shared_ptr<Region> r (_region_view->region ());
4818 framepos_t f = adjusted_current_frame (ev);
4819 f = max (f, r->position ());
4820 f = min (f, r->last_frame ());
4822 _region_view->move_patch_change (
4824 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4829 PatchChangeDrag::aborted (bool)
4831 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4835 PatchChangeDrag::setup_pointer_frame_offset ()
4837 boost::shared_ptr<Region> region = _region_view->region ();
4838 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4841 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4842 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4849 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4851 framepos_t const p = _region_view->region()->position ();
4852 double const y = _region_view->midi_view()->y_position ();
4854 x1 = max ((framepos_t) 0, x1 - p);
4855 x2 = max ((framepos_t) 0, x2 - p);
4856 y1 = max (0.0, y1 - y);
4857 y2 = max (0.0, y2 - y);
4859 _region_view->update_drag_selection (
4860 _editor->sample_to_pixel (x1),
4861 _editor->sample_to_pixel (x2),
4864 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4869 MidiRubberbandSelectDrag::deselect_things ()
4874 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4875 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4878 _vertical_only = true;
4882 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4884 double const y = _region_view->midi_view()->y_position ();
4886 y1 = max (0.0, y1 - y);
4887 y2 = max (0.0, y2 - y);
4889 _region_view->update_vertical_drag_selection (
4892 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4897 MidiVerticalSelectDrag::deselect_things ()
4902 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4903 : RubberbandSelectDrag (e, i)
4909 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4911 if (drag_in_progress) {
4912 /* We just want to select things at the end of the drag, not during it */
4916 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4918 _editor->begin_reversible_command (_("rubberband selection"));
4919 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4920 _editor->commit_reversible_command ();
4924 EditorRubberbandSelectDrag::deselect_things ()
4926 if (!getenv("ARDOUR_SAE")) {
4927 _editor->selection->clear_tracks();
4929 _editor->selection->clear_regions();
4930 _editor->selection->clear_points ();
4931 _editor->selection->clear_lines ();
4934 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4942 NoteCreateDrag::~NoteCreateDrag ()
4948 NoteCreateDrag::grid_frames (framepos_t t) const
4951 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4956 return _region_view->region_beats_to_region_frames (grid_beats);
4960 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4962 Drag::start_grab (event, cursor);
4964 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4966 framepos_t pf = _drags->current_pointer_frame ();
4967 framecnt_t const g = grid_frames (pf);
4969 /* Hack so that we always snap to the note that we are over, instead of snapping
4970 to the next one if we're more than halfway through the one we're over.
4972 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4976 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4978 MidiStreamView* sv = _region_view->midi_stream_view ();
4979 double const x = _editor->sample_to_pixel (_note[0]);
4980 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4982 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4983 _drag_rect->set_outline_all ();
4984 _drag_rect->set_outline_color (0xffffff99);
4985 _drag_rect->set_fill_color (0xffffff66);
4989 NoteCreateDrag::motion (GdkEvent* event, bool)
4991 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4992 double const x = _editor->sample_to_pixel (_note[1]);
4993 if (_note[1] > _note[0]) {
4994 _drag_rect->set_x1 (x);
4996 _drag_rect->set_x0 (x);
5001 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5003 if (!had_movement) {
5007 framepos_t const start = min (_note[0], _note[1]);
5008 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5010 framecnt_t const g = grid_frames (start);
5011 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5013 if (_editor->snap_mode() == SnapNormal && length < g) {
5014 length = g - one_tick;
5017 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5019 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5023 NoteCreateDrag::y_to_region (double y) const
5026 _region_view->get_canvas_group()->canvas_to_item (x, y);
5031 NoteCreateDrag::aborted (bool)
5036 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5041 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5045 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5047 Drag::start_grab (event, cursor);
5051 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5057 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5060 distance = _drags->current_pointer_x() - grab_x();
5061 len = ar->fade_in()->back()->when;
5063 distance = grab_x() - _drags->current_pointer_x();
5064 len = ar->fade_out()->back()->when;
5067 /* how long should it be ? */
5069 new_length = len + _editor->pixel_to_sample (distance);
5071 /* now check with the region that this is legal */
5073 new_length = ar->verify_xfade_bounds (new_length, start);
5076 arv->reset_fade_in_shape_width (ar, new_length);
5078 arv->reset_fade_out_shape_width (ar, new_length);
5083 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5089 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5092 distance = _drags->current_pointer_x() - grab_x();
5093 len = ar->fade_in()->back()->when;
5095 distance = grab_x() - _drags->current_pointer_x();
5096 len = ar->fade_out()->back()->when;
5099 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5101 _editor->begin_reversible_command ("xfade trim");
5102 ar->playlist()->clear_owned_changes ();
5105 ar->set_fade_in_length (new_length);
5107 ar->set_fade_out_length (new_length);
5110 /* Adjusting the xfade may affect other regions in the playlist, so we need
5111 to get undo Commands from the whole playlist rather than just the
5115 vector<Command*> cmds;
5116 ar->playlist()->rdiff (cmds);
5117 _editor->session()->add_commands (cmds);
5118 _editor->commit_reversible_command ();
5123 CrossfadeEdgeDrag::aborted (bool)
5126 arv->redraw_start_xfade ();
5128 arv->redraw_end_xfade ();