2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
46 #include "audio_region_view.h"
47 #include "midi_region_view.h"
48 #include "ardour_ui.h"
49 #include "gui_thread.h"
50 #include "control_point.h"
52 #include "region_gain_line.h"
53 #include "editor_drag.h"
54 #include "audio_time_axis.h"
55 #include "midi_time_axis.h"
56 #include "selection.h"
57 #include "midi_selection.h"
58 #include "automation_time_axis.h"
60 #include "editor_cursors.h"
61 #include "mouse_cursors.h"
62 #include "note_base.h"
63 #include "patch_change.h"
64 #include "verbose_cursor.h"
67 using namespace ARDOUR;
70 using namespace Gtkmm2ext;
71 using namespace Editing;
72 using namespace ArdourCanvas;
74 using Gtkmm2ext::Keyboard;
76 double ControlPointDrag::_zero_gain_fraction = -1.0;
78 DragManager::DragManager (Editor* e)
81 , _current_pointer_frame (0)
85 DragManager::~DragManager ()
90 /** Call abort for each active drag */
96 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
101 if (!_drags.empty ()) {
102 _editor->set_follow_playhead (_old_follow_playhead, false);
111 DragManager::add (Drag* d)
113 d->set_manager (this);
114 _drags.push_back (d);
118 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
120 d->set_manager (this);
121 _drags.push_back (d);
126 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
128 /* Prevent follow playhead during the drag to be nice to the user */
129 _old_follow_playhead = _editor->follow_playhead ();
130 _editor->set_follow_playhead (false);
132 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
134 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
135 (*i)->start_grab (e, c);
139 /** Call end_grab for each active drag.
140 * @return true if any drag reported movement having occurred.
143 DragManager::end_grab (GdkEvent* e)
148 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
149 bool const t = (*i)->end_grab (e);
160 _editor->set_follow_playhead (_old_follow_playhead, false);
166 DragManager::mark_double_click ()
168 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
169 (*i)->set_double_click (true);
174 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
178 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
180 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
181 bool const t = (*i)->motion_handler (e, from_autoscroll);
192 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
196 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
198 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
199 bool const t = (*i)->motion_handler (e, from_autoscroll);
210 DragManager::have_item (ArdourCanvas::Item* i) const
212 list<Drag*>::const_iterator j = _drags.begin ();
213 while (j != _drags.end() && (*j)->item () != i) {
217 return j != _drags.end ();
220 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
223 , _pointer_frame_offset (0)
224 , _move_threshold_passed (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
234 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
247 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
249 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
251 if (Keyboard::is_button2_event (&event->button)) {
252 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
253 _y_constrained = true;
254 _x_constrained = false;
256 _y_constrained = false;
257 _x_constrained = true;
260 _x_constrained = false;
261 _y_constrained = false;
264 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
265 setup_pointer_frame_offset ();
266 _grab_frame = adjusted_frame (_raw_grab_frame, event);
267 _last_pointer_frame = _grab_frame;
268 _last_pointer_x = _grab_x;
269 _last_pointer_y = _grab_y;
275 /* CAIROCANVAS need a variant here that passes *cursor */
280 if (_editor->session() && _editor->session()->transport_rolling()) {
283 _was_rolling = false;
286 switch (_editor->snap_type()) {
287 case SnapToRegionStart:
288 case SnapToRegionEnd:
289 case SnapToRegionSync:
290 case SnapToRegionBoundary:
291 _editor->build_region_boundary_cache ();
298 /** Call to end a drag `successfully'. Ungrabs item and calls
299 * subclass' finished() method.
301 * @param event GDK event, or 0.
302 * @return true if some movement occurred, otherwise false.
305 Drag::end_grab (GdkEvent* event)
307 _editor->stop_canvas_autoscroll ();
311 finished (event, _move_threshold_passed);
313 _editor->verbose_cursor()->hide ();
315 return _move_threshold_passed;
319 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
323 if (f > _pointer_frame_offset) {
324 pos = f - _pointer_frame_offset;
328 _editor->snap_to_with_modifier (pos, event);
335 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
337 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
341 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
343 /* check to see if we have moved in any way that matters since the last motion event */
344 if (_move_threshold_passed &&
345 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
346 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
350 pair<framecnt_t, int> const threshold = move_threshold ();
352 bool const old_move_threshold_passed = _move_threshold_passed;
354 if (!from_autoscroll && !_move_threshold_passed) {
356 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
357 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
359 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
362 if (active (_editor->mouse_mode) && _move_threshold_passed) {
364 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
365 if (!from_autoscroll) {
366 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
369 if (!_editor->autoscroll_active() || from_autoscroll) {
370 motion (event, _move_threshold_passed != old_move_threshold_passed);
372 _last_pointer_x = _drags->current_pointer_x ();
373 _last_pointer_y = _drags->current_pointer_y ();
374 _last_pointer_frame = adjusted_current_frame (event);
383 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
391 aborted (_move_threshold_passed);
393 _editor->stop_canvas_autoscroll ();
394 _editor->verbose_cursor()->hide ();
398 Drag::show_verbose_cursor_time (framepos_t frame)
400 _editor->verbose_cursor()->set_time (
402 _drags->current_pointer_x() + 10,
403 _drags->current_pointer_y() + 10
406 _editor->verbose_cursor()->show ();
410 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
412 _editor->verbose_cursor()->show (xoffset);
414 _editor->verbose_cursor()->set_duration (
416 _drags->current_pointer_x() + 10,
417 _drags->current_pointer_y() + 10
422 Drag::show_verbose_cursor_text (string const & text)
424 _editor->verbose_cursor()->show ();
426 _editor->verbose_cursor()->set (
428 _drags->current_pointer_x() + 10,
429 _drags->current_pointer_y() + 10
433 boost::shared_ptr<Region>
434 Drag::add_midi_region (MidiTimeAxisView* view)
436 if (_editor->session()) {
437 const TempoMap& map (_editor->session()->tempo_map());
438 framecnt_t pos = grab_frame();
439 const Meter& m = map.meter_at (pos);
440 /* not that the frame rate used here can be affected by pull up/down which
443 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
444 return view->add_region (grab_frame(), len, true);
447 return boost::shared_ptr<Region>();
450 struct EditorOrderTimeAxisViewSorter {
451 bool operator() (TimeAxisView* a, TimeAxisView* b) {
452 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
453 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
455 return ra->route()->order_key () < rb->route()->order_key ();
459 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
463 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
465 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
466 as some of the regions we are dragging may be on such tracks.
469 TrackViewList track_views = _editor->track_views;
470 track_views.sort (EditorOrderTimeAxisViewSorter ());
472 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
473 _time_axis_views.push_back (*i);
475 TimeAxisView::Children children_list = (*i)->get_child_list ();
476 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
477 _time_axis_views.push_back (j->get());
481 /* the list of views can be empty at this point if this is a region list-insert drag
484 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
485 _views.push_back (DraggingView (*i, this));
488 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
492 RegionDrag::region_going_away (RegionView* v)
494 list<DraggingView>::iterator i = _views.begin ();
495 while (i != _views.end() && i->view != v) {
499 if (i != _views.end()) {
504 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
505 * or -1 if it is not found.
508 RegionDrag::find_time_axis_view (TimeAxisView* t) const
511 int const N = _time_axis_views.size ();
512 while (i < N && _time_axis_views[i] != t) {
523 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
524 : RegionDrag (e, i, p, v)
527 , _last_pointer_time_axis_view (0)
528 , _last_pointer_layer (0)
530 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
534 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
536 Drag::start_grab (event, cursor);
538 show_verbose_cursor_time (_last_frame_position);
540 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
542 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
543 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
548 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
550 /* compute the amount of pointer motion in frames, and where
551 the region would be if we moved it by that much.
553 *pending_region_position = adjusted_current_frame (event);
555 framepos_t sync_frame;
556 framecnt_t sync_offset;
559 sync_offset = _primary->region()->sync_offset (sync_dir);
561 /* we don't handle a sync point that lies before zero.
563 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
565 sync_frame = *pending_region_position + (sync_dir*sync_offset);
567 _editor->snap_to_with_modifier (sync_frame, event);
569 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
572 *pending_region_position = _last_frame_position;
575 if (*pending_region_position > max_framepos - _primary->region()->length()) {
576 *pending_region_position = _last_frame_position;
581 /* in locked edit mode, reverse the usual meaning of _x_constrained */
582 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
584 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
586 /* x movement since last time (in pixels) */
587 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
589 /* total x movement */
590 framecnt_t total_dx = *pending_region_position;
591 if (regions_came_from_canvas()) {
592 total_dx = total_dx - grab_frame ();
595 /* check that no regions have gone off the start of the session */
596 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
597 if ((i->view->region()->position() + total_dx) < 0) {
599 *pending_region_position = _last_frame_position;
604 _last_frame_position = *pending_region_position;
611 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
613 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
614 int const n = i->time_axis_view + delta_track;
615 if (n < 0 || n >= int (_time_axis_views.size())) {
616 /* off the top or bottom track */
620 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
621 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
622 /* not a track, or the wrong type */
626 double const l = i->layer + delta_layer;
628 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
629 mode to allow the user to place a region below another on layer 0.
631 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
632 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
633 If it has, the layers will be munged later anyway, so it's ok.
639 /* all regions being dragged are ok with this change */
644 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
646 double delta_layer = 0;
647 int delta_time_axis_view = 0;
649 assert (!_views.empty ());
651 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
653 /* Find the TimeAxisView that the pointer is now over */
654 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (_drags->current_pointer_y ());
655 TimeAxisView* tv = r.first;
659 double layer = r.second;
661 if (first_move && tv->view()->layer_display() == Stacked) {
662 tv->view()->set_layer_display (Expanded);
665 /* Here's the current pointer position in terms of time axis view and layer */
666 int const current_pointer_time_axis_view = find_time_axis_view (tv);
667 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
669 /* Work out the change in y */
671 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
672 delta_layer = current_pointer_layer - _last_pointer_layer;
675 /* Work out the change in x */
676 framepos_t pending_region_position;
677 double const x_delta = compute_x_delta (event, &pending_region_position);
679 /* Verify change in y */
680 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
681 /* this y movement is not allowed, so do no y movement this time */
682 delta_time_axis_view = 0;
686 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
687 /* haven't reached next snap point, and we're not switching
688 trackviews nor layers. nothing to do.
693 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
695 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
697 RegionView* rv = i->view;
699 if (rv->region()->locked() || rv->region()->video_locked()) {
707 /* Reparent to a non scrolling group so that we can keep the
708 region selection above all time axis views.
709 Reparenting means that we will have to move the region view
710 within its new parent, as the two parent groups have different coordinates.
713 ArdourCanvas::Group* rvg = rv->get_canvas_group();
714 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
716 rv->get_canvas_group()->reparent (_editor->_region_motion_group);
718 rv->fake_set_opaque (true);
719 rvg->set_position (rv_canvas_offset);
722 /* If we have moved tracks, we'll fudge the layer delta so that the
723 region gets moved back onto layer 0 on its new track; this avoids
724 confusion when dragging regions from non-zero layers onto different
727 double this_delta_layer = delta_layer;
728 if (delta_time_axis_view != 0) {
729 this_delta_layer = - i->layer;
734 /* The TimeAxisView that this region is now on */
735 TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
737 /* Ensure it is moved from stacked -> expanded if appropriate */
738 if (current_tv->view()->layer_display() == Stacked) {
739 current_tv->view()->set_layer_display (Expanded);
742 /* We're only allowed to go -ve in layer on Expanded views */
743 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
744 this_delta_layer = - i->layer;
748 rv->set_height (current_tv->view()->child_height ());
750 /* Update show/hidden status as the region view may have come from a hidden track,
751 or have moved to one.
753 if (current_tv->hidden ()) {
754 rv->get_canvas_group()->hide ();
756 rv->get_canvas_group()->show ();
759 /* Update the DraggingView */
760 i->time_axis_view += delta_time_axis_view;
761 i->layer += this_delta_layer;
764 _editor->mouse_brush_insert_region (rv, pending_region_position);
769 /* Get the y coordinate of the top of the track that this region is now on */
770 current_tv->canvas_display()->item_to_canvas (x, y);
772 /* And adjust for the layer that it should be on */
773 StreamView* cv = current_tv->view ();
774 switch (cv->layer_display ()) {
778 y += (cv->layers() - i->layer - 1) * cv->child_height ();
781 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
785 /* Now move the region view */
786 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
792 TimeAxisView* last = _time_axis_views.back();
793 last->canvas_display()->item_to_canvas (x, y);
794 y += last->effective_height();
795 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
796 i->time_axis_view = -1;
799 } /* foreach region */
801 _total_x_delta += x_delta;
803 if (x_delta != 0 && !_brushing) {
804 show_verbose_cursor_time (_last_frame_position);
807 _last_pointer_time_axis_view += delta_time_axis_view;
808 _last_pointer_layer += delta_layer;
812 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
814 if (_copy && first_move) {
816 /* duplicate the regionview(s) and region(s) */
818 list<DraggingView> new_regionviews;
820 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
822 RegionView* rv = i->view;
823 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
824 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
826 const boost::shared_ptr<const Region> original = rv->region();
827 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
828 region_copy->set_position (original->position());
832 boost::shared_ptr<AudioRegion> audioregion_copy
833 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
835 nrv = new AudioRegionView (*arv, audioregion_copy);
837 boost::shared_ptr<MidiRegion> midiregion_copy
838 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
839 nrv = new MidiRegionView (*mrv, midiregion_copy);
844 nrv->get_canvas_group()->show ();
845 new_regionviews.push_back (DraggingView (nrv, this));
847 /* swap _primary to the copy */
849 if (rv == _primary) {
853 /* ..and deselect the one we copied */
855 rv->set_selected (false);
858 if (!new_regionviews.empty()) {
860 /* reflect the fact that we are dragging the copies */
862 _views = new_regionviews;
864 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
868 RegionMotionDrag::motion (event, first_move);
872 RegionMotionDrag::finished (GdkEvent *, bool)
874 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
879 if ((*i)->view()->layer_display() == Expanded) {
880 (*i)->view()->set_layer_display (Stacked);
886 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
888 RegionMotionDrag::finished (ev, movement_occurred);
890 if (!movement_occurred) {
894 if (was_double_click() && !_views.empty()) {
895 DraggingView dv = _views.front();
896 dv.view->show_region_editor ();
903 /* reverse this here so that we have the correct logic to finalize
907 if (Config->get_edit_mode() == Lock) {
908 _x_constrained = !_x_constrained;
911 assert (!_views.empty ());
913 /* We might have hidden region views so that they weren't visible during the drag
914 (when they have been reparented). Now everything can be shown again, as region
915 views are back in their track parent groups.
917 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
918 i->view->get_canvas_group()->show ();
921 bool const changed_position = (_last_frame_position != _primary->region()->position());
922 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
923 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
943 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
947 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
949 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
954 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
955 list<boost::shared_ptr<AudioTrack> > audio_tracks;
956 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
957 return _editor->axis_view_from_route (audio_tracks.front());
959 ChanCount one_midi_port (DataType::MIDI, 1);
960 list<boost::shared_ptr<MidiTrack> > midi_tracks;
961 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
962 return _editor->axis_view_from_route (midi_tracks.front());
965 error << _("Could not create new track after region placed in the drop zone") << endmsg;
971 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
973 RegionSelection new_views;
974 PlaylistSet modified_playlists;
975 RouteTimeAxisView* new_time_axis_view = 0;
978 /* all changes were made during motion event handlers */
980 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
984 _editor->commit_reversible_command ();
988 if (_x_constrained) {
989 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
991 _editor->begin_reversible_command (Operations::region_copy);
994 /* insert the regions into their new playlists */
995 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
997 RouteTimeAxisView* dest_rtv = 0;
999 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1005 if (changed_position && !_x_constrained) {
1006 where = i->view->region()->position() - drag_delta;
1008 where = i->view->region()->position();
1011 if (i->time_axis_view < 0) {
1012 if (!new_time_axis_view) {
1013 new_time_axis_view = create_destination_time_axis (i->view->region());
1015 dest_rtv = new_time_axis_view;
1017 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1020 if (dest_rtv != 0) {
1021 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1022 if (new_view != 0) {
1023 new_views.push_back (new_view);
1027 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1028 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1031 list<DraggingView>::const_iterator next = i;
1037 /* If we've created new regions either by copying or moving
1038 to a new track, we want to replace the old selection with the new ones
1041 if (new_views.size() > 0) {
1042 _editor->selection->set (new_views);
1045 /* write commands for the accumulated diffs for all our modified playlists */
1046 add_stateful_diff_commands_for_playlists (modified_playlists);
1048 _editor->commit_reversible_command ();
1052 RegionMoveDrag::finished_no_copy (
1053 bool const changed_position,
1054 bool const changed_tracks,
1055 framecnt_t const drag_delta
1058 RegionSelection new_views;
1059 PlaylistSet modified_playlists;
1060 PlaylistSet frozen_playlists;
1061 set<RouteTimeAxisView*> views_to_update;
1062 RouteTimeAxisView* new_time_axis_view = 0;
1065 /* all changes were made during motion event handlers */
1066 _editor->commit_reversible_command ();
1070 if (_x_constrained) {
1071 _editor->begin_reversible_command (_("fixed time region drag"));
1073 _editor->begin_reversible_command (Operations::region_drag);
1076 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1078 RegionView* rv = i->view;
1079 RouteTimeAxisView* dest_rtv = 0;
1081 if (rv->region()->locked() || rv->region()->video_locked()) {
1086 if (i->time_axis_view < 0) {
1087 if (!new_time_axis_view) {
1088 new_time_axis_view = create_destination_time_axis (rv->region());
1090 dest_rtv = new_time_axis_view;
1092 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1097 double const dest_layer = i->layer;
1099 views_to_update.insert (dest_rtv);
1103 if (changed_position && !_x_constrained) {
1104 where = rv->region()->position() - drag_delta;
1106 where = rv->region()->position();
1109 if (changed_tracks) {
1111 /* insert into new playlist */
1113 RegionView* new_view = insert_region_into_playlist (
1114 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1117 if (new_view == 0) {
1122 new_views.push_back (new_view);
1124 /* remove from old playlist */
1126 /* the region that used to be in the old playlist is not
1127 moved to the new one - we use a copy of it. as a result,
1128 any existing editor for the region should no longer be
1131 rv->hide_region_editor();
1132 rv->fake_set_opaque (false);
1134 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1138 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1140 /* this movement may result in a crossfade being modified, or a layering change,
1141 so we need to get undo data from the playlist as well as the region.
1144 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1146 playlist->clear_changes ();
1149 rv->region()->clear_changes ();
1152 motion on the same track. plonk the previously reparented region
1153 back to its original canvas group (its streamview).
1154 No need to do anything for copies as they are fake regions which will be deleted.
1157 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1158 rv->get_canvas_group()->set_y_position (i->initial_y);
1161 /* just change the model */
1162 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1163 playlist->set_layer (rv->region(), dest_layer);
1166 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1168 r = frozen_playlists.insert (playlist);
1171 playlist->freeze ();
1174 rv->region()->set_position (where);
1176 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1179 if (changed_tracks) {
1181 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1182 was selected in all of them, then removing it from a playlist will have removed all
1183 trace of it from _views (i.e. there were N regions selected, we removed 1,
1184 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1185 corresponding regionview, and _views is now empty).
1187 This could have invalidated any and all iterators into _views.
1189 The heuristic we use here is: if the region selection is empty, break out of the loop
1190 here. if the region selection is not empty, then restart the loop because we know that
1191 we must have removed at least the region(view) we've just been working on as well as any
1192 that we processed on previous iterations.
1194 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1195 we can just iterate.
1199 if (_views.empty()) {
1210 /* If we've created new regions either by copying or moving
1211 to a new track, we want to replace the old selection with the new ones
1214 if (new_views.size() > 0) {
1215 _editor->selection->set (new_views);
1218 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1222 /* write commands for the accumulated diffs for all our modified playlists */
1223 add_stateful_diff_commands_for_playlists (modified_playlists);
1225 _editor->commit_reversible_command ();
1227 /* We have futzed with the layering of canvas items on our streamviews.
1228 If any region changed layer, this will have resulted in the stream
1229 views being asked to set up their region views, and all will be well.
1230 If not, we might now have badly-ordered region views. Ask the StreamViews
1231 involved to sort themselves out, just in case.
1234 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1235 (*i)->view()->playlist_layered ((*i)->track ());
1239 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1240 * @param region Region to remove.
1241 * @param playlist playlist To remove from.
1242 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1243 * that clear_changes () is only called once per playlist.
1246 RegionMoveDrag::remove_region_from_playlist (
1247 boost::shared_ptr<Region> region,
1248 boost::shared_ptr<Playlist> playlist,
1249 PlaylistSet& modified_playlists
1252 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1255 playlist->clear_changes ();
1258 playlist->remove_region (region);
1262 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1263 * clearing the playlist's diff history first if necessary.
1264 * @param region Region to insert.
1265 * @param dest_rtv Destination RouteTimeAxisView.
1266 * @param dest_layer Destination layer.
1267 * @param where Destination position.
1268 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1269 * that clear_changes () is only called once per playlist.
1270 * @return New RegionView, or 0 if no insert was performed.
1273 RegionMoveDrag::insert_region_into_playlist (
1274 boost::shared_ptr<Region> region,
1275 RouteTimeAxisView* dest_rtv,
1278 PlaylistSet& modified_playlists
1281 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1282 if (!dest_playlist) {
1286 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1287 _new_region_view = 0;
1288 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1290 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1291 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1293 dest_playlist->clear_changes ();
1296 dest_playlist->add_region (region, where);
1298 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1299 dest_playlist->set_layer (region, dest_layer);
1304 assert (_new_region_view);
1306 return _new_region_view;
1310 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1312 _new_region_view = rv;
1316 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1318 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1319 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1321 _editor->session()->add_command (c);
1330 RegionMoveDrag::aborted (bool movement_occurred)
1334 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1341 RegionMotionDrag::aborted (movement_occurred);
1346 RegionMotionDrag::aborted (bool)
1348 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1350 StreamView* sview = (*i)->view();
1353 if (sview->layer_display() == Expanded) {
1354 sview->set_layer_display (Stacked);
1359 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1360 RegionView* rv = i->view;
1361 TimeAxisView* tv = &(rv->get_time_axis_view ());
1362 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1364 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1365 rv->get_canvas_group()->set_y_position (0);
1367 rv->fake_set_opaque (false);
1368 rv->move (-_total_x_delta, 0);
1369 rv->set_height (rtv->view()->child_height ());
1373 /** @param b true to brush, otherwise false.
1374 * @param c true to make copies of the regions being moved, otherwise false.
1376 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1377 : RegionMotionDrag (e, i, p, v, b),
1380 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1383 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1384 if (rtv && rtv->is_track()) {
1385 speed = rtv->track()->speed ();
1388 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1392 RegionMoveDrag::setup_pointer_frame_offset ()
1394 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1397 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1398 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1400 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1402 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1403 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1405 _primary = v->view()->create_region_view (r, false, false);
1407 _primary->get_canvas_group()->show ();
1408 _primary->set_position (pos, 0);
1409 _views.push_back (DraggingView (_primary, this));
1411 _last_frame_position = pos;
1413 _item = _primary->get_canvas_group ();
1417 RegionInsertDrag::finished (GdkEvent *, bool)
1419 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1421 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1422 _primary->get_canvas_group()->set_y_position (0);
1424 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1426 _editor->begin_reversible_command (Operations::insert_region);
1427 playlist->clear_changes ();
1428 playlist->add_region (_primary->region (), _last_frame_position);
1429 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1430 _editor->commit_reversible_command ();
1438 RegionInsertDrag::aborted (bool)
1445 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1446 : RegionMoveDrag (e, i, p, v, false, false)
1448 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1451 struct RegionSelectionByPosition {
1452 bool operator() (RegionView*a, RegionView* b) {
1453 return a->region()->position () < b->region()->position();
1458 RegionSpliceDrag::motion (GdkEvent* event, bool)
1460 /* Which trackview is this ? */
1462 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1463 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1465 /* The region motion is only processed if the pointer is over
1469 if (!tv || !tv->is_track()) {
1470 /* To make sure we hide the verbose canvas cursor when the mouse is
1471 not held over and audiotrack.
1473 _editor->verbose_cursor()->hide ();
1479 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1485 RegionSelection copy (_editor->selection->regions);
1487 RegionSelectionByPosition cmp;
1490 framepos_t const pf = adjusted_current_frame (event);
1492 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1494 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1500 boost::shared_ptr<Playlist> playlist;
1502 if ((playlist = atv->playlist()) == 0) {
1506 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1511 if (pf < (*i)->region()->last_frame() + 1) {
1515 if (pf > (*i)->region()->first_frame()) {
1521 playlist->shuffle ((*i)->region(), dir);
1526 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1528 RegionMoveDrag::finished (event, movement_occurred);
1532 RegionSpliceDrag::aborted (bool)
1537 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1539 _view (dynamic_cast<MidiTimeAxisView*> (v))
1541 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1547 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1550 _region = add_midi_region (_view);
1551 _view->playlist()->freeze ();
1554 framepos_t const f = adjusted_current_frame (event);
1555 if (f < grab_frame()) {
1556 _region->set_position (f);
1559 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1560 so that if this region is duplicated, its duplicate starts on
1561 a snap point rather than 1 frame after a snap point. Otherwise things get
1562 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1563 place snapped notes at the start of the region.
1566 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1567 _region->set_length (len < 1 ? 1 : len);
1573 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1575 if (!movement_occurred) {
1576 add_midi_region (_view);
1578 _view->playlist()->thaw ();
1583 RegionCreateDrag::aborted (bool)
1586 _view->playlist()->thaw ();
1592 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1596 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1600 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1602 Gdk::Cursor* cursor;
1603 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1605 float x_fraction = cnote->mouse_x_fraction ();
1607 if (x_fraction > 0.0 && x_fraction < 0.25) {
1608 cursor = _editor->cursors()->left_side_trim;
1610 cursor = _editor->cursors()->right_side_trim;
1613 Drag::start_grab (event, cursor);
1615 region = &cnote->region_view();
1617 double const region_start = region->get_position_pixels();
1618 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1620 if (grab_x() <= middle_point) {
1621 cursor = _editor->cursors()->left_side_trim;
1624 cursor = _editor->cursors()->right_side_trim;
1630 if (event->motion.state & Keyboard::PrimaryModifier) {
1636 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1638 if (ms.size() > 1) {
1639 /* has to be relative, may make no sense otherwise */
1643 /* select this note; if it is already selected, preserve the existing selection,
1644 otherwise make this note the only one selected.
1646 region->note_selected (cnote, cnote->selected ());
1648 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1649 MidiRegionSelection::iterator next;
1652 (*r)->begin_resizing (at_front);
1658 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1660 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1661 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1662 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1664 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1669 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1671 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1672 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1673 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1675 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1680 NoteResizeDrag::aborted (bool)
1682 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1683 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1684 (*r)->abort_resizing ();
1688 AVDraggingView::AVDraggingView (RegionView* v)
1691 initial_position = v->region()->position ();
1694 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1697 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1700 TrackViewList empty;
1702 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1703 std::list<RegionView*> views = rs.by_layer();
1705 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1706 RegionView* rv = (*i);
1707 if (!rv->region()->video_locked()) {
1710 _views.push_back (AVDraggingView (rv));
1715 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1717 Drag::start_grab (event);
1718 if (_editor->session() == 0) {
1722 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1723 _max_backwards_drag = (
1724 ARDOUR_UI::instance()->video_timeline->get_duration()
1725 + ARDOUR_UI::instance()->video_timeline->get_offset()
1726 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1729 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1730 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1731 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1734 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1737 Timecode::Time timecode;
1738 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1739 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);
1740 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1741 _editor->verbose_cursor()->show ();
1745 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1747 if (_editor->session() == 0) {
1750 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1754 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1755 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1757 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1758 dt = - _max_backwards_drag;
1761 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1762 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1764 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1765 RegionView* rv = i->view;
1766 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1769 rv->fake_set_opaque (true);
1770 rv->region()->clear_changes ();
1771 rv->region()->suspend_property_changes();
1773 rv->region()->set_position(i->initial_position + dt);
1774 rv->region_changed(ARDOUR::Properties::position);
1777 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1778 Timecode::Time timecode;
1779 Timecode::Time timediff;
1781 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1782 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1783 snprintf (buf, sizeof (buf),
1784 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1785 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1786 , _("Video Start:"),
1787 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1789 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1791 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1792 _editor->verbose_cursor()->show ();
1796 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1798 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1802 if (!movement_occurred || ! _editor->session()) {
1806 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1808 _editor->begin_reversible_command (_("Move Video"));
1810 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1811 ARDOUR_UI::instance()->video_timeline->save_undo();
1812 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1813 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1815 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1816 i->view->drag_end();
1817 i->view->fake_set_opaque (false);
1818 i->view->region()->resume_property_changes ();
1820 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1823 _editor->session()->maybe_update_session_range(
1824 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1825 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1829 _editor->commit_reversible_command ();
1833 VideoTimeLineDrag::aborted (bool)
1835 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1838 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1839 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1841 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1842 i->view->region()->resume_property_changes ();
1843 i->view->region()->set_position(i->initial_position);
1847 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1848 : RegionDrag (e, i, p, v)
1849 , _preserve_fade_anchor (preserve_fade_anchor)
1850 , _jump_position_when_done (false)
1852 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1856 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1859 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1860 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1862 if (tv && tv->is_track()) {
1863 speed = tv->track()->speed();
1866 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1867 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1868 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1870 framepos_t const pf = adjusted_current_frame (event);
1872 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1873 /* Move the contents of the region around without changing the region bounds */
1874 _operation = ContentsTrim;
1875 Drag::start_grab (event, _editor->cursors()->trimmer);
1877 /* These will get overridden for a point trim.*/
1878 if (pf < (region_start + region_length/2)) {
1879 /* closer to front */
1880 _operation = StartTrim;
1881 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1884 _operation = EndTrim;
1885 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1889 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1890 _jump_position_when_done = true;
1893 switch (_operation) {
1895 show_verbose_cursor_time (region_start);
1896 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1897 i->view->trim_front_starting ();
1901 show_verbose_cursor_time (region_end);
1904 show_verbose_cursor_time (pf);
1908 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1909 i->view->region()->suspend_property_changes ();
1914 TrimDrag::motion (GdkEvent* event, bool first_move)
1916 RegionView* rv = _primary;
1919 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1920 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1921 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1922 frameoffset_t frame_delta = 0;
1924 if (tv && tv->is_track()) {
1925 speed = tv->track()->speed();
1928 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1934 switch (_operation) {
1936 trim_type = "Region start trim";
1939 trim_type = "Region end trim";
1942 trim_type = "Region content trim";
1949 _editor->begin_reversible_command (trim_type);
1951 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1952 RegionView* rv = i->view;
1953 rv->fake_set_opaque (false);
1954 rv->enable_display (false);
1955 rv->region()->playlist()->clear_owned_changes ();
1957 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1960 arv->temporarily_hide_envelope ();
1964 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1965 insert_result = _editor->motion_frozen_playlists.insert (pl);
1967 if (insert_result.second) {
1973 bool non_overlap_trim = false;
1975 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1976 non_overlap_trim = true;
1979 /* contstrain trim to fade length */
1980 if (_preserve_fade_anchor) {
1981 switch (_operation) {
1983 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1984 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1986 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1987 if (ar->locked()) continue;
1988 framecnt_t len = ar->fade_in()->back()->when;
1989 if (len < dt) dt = min(dt, len);
1993 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1994 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1996 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1997 if (ar->locked()) continue;
1998 framecnt_t len = ar->fade_out()->back()->when;
1999 if (len < -dt) dt = max(dt, -len);
2008 switch (_operation) {
2010 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2011 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2012 if (changed && _preserve_fade_anchor) {
2013 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2015 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2016 framecnt_t len = ar->fade_in()->back()->when;
2017 framecnt_t diff = ar->first_frame() - i->initial_position;
2018 framepos_t new_length = len - diff;
2019 i->anchored_fade_length = min (ar->length(), new_length);
2020 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2021 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2028 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2029 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2030 if (changed && _preserve_fade_anchor) {
2031 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2033 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2034 framecnt_t len = ar->fade_out()->back()->when;
2035 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2036 framepos_t new_length = len + diff;
2037 i->anchored_fade_length = min (ar->length(), new_length);
2038 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2039 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2047 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2049 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2050 i->view->move_contents (frame_delta);
2056 switch (_operation) {
2058 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2061 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2064 // show_verbose_cursor_time (frame_delta);
2071 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2073 if (movement_occurred) {
2074 motion (event, false);
2076 if (_operation == StartTrim) {
2077 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2079 /* This must happen before the region's StatefulDiffCommand is created, as it may
2080 `correct' (ahem) the region's _start from being negative to being zero. It
2081 needs to be zero in the undo record.
2083 i->view->trim_front_ending ();
2085 if (_preserve_fade_anchor) {
2086 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2088 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2089 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2090 ar->set_fade_in_length(i->anchored_fade_length);
2091 ar->set_fade_in_active(true);
2094 if (_jump_position_when_done) {
2095 i->view->region()->set_position (i->initial_position);
2098 } else if (_operation == EndTrim) {
2099 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2100 if (_preserve_fade_anchor) {
2101 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2103 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2104 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2105 ar->set_fade_out_length(i->anchored_fade_length);
2106 ar->set_fade_out_active(true);
2109 if (_jump_position_when_done) {
2110 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2115 if (!_views.empty()) {
2116 if (_operation == StartTrim) {
2117 _editor->maybe_locate_with_edit_preroll(
2118 _views.begin()->view->region()->position());
2120 if (_operation == EndTrim) {
2121 _editor->maybe_locate_with_edit_preroll(
2122 _views.begin()->view->region()->position() +
2123 _views.begin()->view->region()->length());
2127 if (!_editor->selection->selected (_primary)) {
2128 _primary->thaw_after_trim ();
2131 set<boost::shared_ptr<Playlist> > diffed_playlists;
2133 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2134 i->view->thaw_after_trim ();
2135 i->view->enable_display (true);
2136 i->view->fake_set_opaque (true);
2138 /* Trimming one region may affect others on the playlist, so we need
2139 to get undo Commands from the whole playlist rather than just the
2140 region. Use diffed_playlists to make sure we don't diff a given
2141 playlist more than once.
2143 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2144 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2145 vector<Command*> cmds;
2147 _editor->session()->add_commands (cmds);
2148 diffed_playlists.insert (p);
2153 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2157 _editor->motion_frozen_playlists.clear ();
2158 _editor->commit_reversible_command();
2161 /* no mouse movement */
2162 _editor->point_trim (event, adjusted_current_frame (event));
2165 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2166 if (_operation == StartTrim) {
2167 i->view->trim_front_ending ();
2170 i->view->region()->resume_property_changes ();
2175 TrimDrag::aborted (bool movement_occurred)
2177 /* Our motion method is changing model state, so use the Undo system
2178 to cancel. Perhaps not ideal, as this will leave an Undo point
2179 behind which may be slightly odd from the user's point of view.
2184 if (movement_occurred) {
2188 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2189 i->view->region()->resume_property_changes ();
2194 TrimDrag::setup_pointer_frame_offset ()
2196 list<DraggingView>::iterator i = _views.begin ();
2197 while (i != _views.end() && i->view != _primary) {
2201 if (i == _views.end()) {
2205 switch (_operation) {
2207 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2210 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2217 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2221 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2222 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2227 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2229 Drag::start_grab (event, cursor);
2230 show_verbose_cursor_time (adjusted_current_frame(event));
2234 MeterMarkerDrag::setup_pointer_frame_offset ()
2236 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2240 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2242 if (!_marker->meter().movable()) {
2248 // create a dummy marker for visual representation of moving the
2249 // section, because whether its a copy or not, we're going to
2250 // leave or lose the original marker (leave if its a copy; lose if its
2251 // not, because we'll remove it from the map).
2253 MeterSection section (_marker->meter());
2255 if (!section.movable()) {
2260 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2262 _marker = new MeterMarker (
2264 *_editor->meter_group,
2265 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2267 *new MeterSection (_marker->meter())
2270 /* use the new marker for the grab */
2271 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2274 TempoMap& map (_editor->session()->tempo_map());
2275 /* get current state */
2276 before_state = &map.get_state();
2277 /* remove the section while we drag it */
2278 map.remove_meter (section, true);
2282 framepos_t const pf = adjusted_current_frame (event);
2283 _marker->set_position (pf);
2284 show_verbose_cursor_time (pf);
2288 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2290 if (!movement_occurred) {
2291 if (was_double_click()) {
2292 _editor->edit_meter_marker (*_marker);
2297 if (!_marker->meter().movable()) {
2301 motion (event, false);
2303 Timecode::BBT_Time when;
2305 TempoMap& map (_editor->session()->tempo_map());
2306 map.bbt_time (last_pointer_frame(), when);
2308 if (_copy == true) {
2309 _editor->begin_reversible_command (_("copy meter mark"));
2310 XMLNode &before = map.get_state();
2311 map.add_meter (_marker->meter(), when);
2312 XMLNode &after = map.get_state();
2313 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2314 _editor->commit_reversible_command ();
2317 _editor->begin_reversible_command (_("move meter mark"));
2319 /* we removed it before, so add it back now */
2321 map.add_meter (_marker->meter(), when);
2322 XMLNode &after = map.get_state();
2323 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2324 _editor->commit_reversible_command ();
2327 // delete the dummy marker we used for visual representation while moving.
2328 // a new visual marker will show up automatically.
2333 MeterMarkerDrag::aborted (bool moved)
2335 _marker->set_position (_marker->meter().frame ());
2338 TempoMap& map (_editor->session()->tempo_map());
2339 /* we removed it before, so add it back now */
2340 map.add_meter (_marker->meter(), _marker->meter().frame());
2341 // delete the dummy marker we used for visual representation while moving.
2342 // a new visual marker will show up automatically.
2347 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2351 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2353 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2358 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2360 Drag::start_grab (event, cursor);
2361 show_verbose_cursor_time (adjusted_current_frame (event));
2365 TempoMarkerDrag::setup_pointer_frame_offset ()
2367 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2371 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2373 if (!_marker->tempo().movable()) {
2379 // create a dummy marker for visual representation of moving the
2380 // section, because whether its a copy or not, we're going to
2381 // leave or lose the original marker (leave if its a copy; lose if its
2382 // not, because we'll remove it from the map).
2384 // create a dummy marker for visual representation of moving the copy.
2385 // The actual copying is not done before we reach the finish callback.
2388 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2390 TempoSection section (_marker->tempo());
2392 _marker = new TempoMarker (
2394 *_editor->tempo_group,
2395 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2397 *new TempoSection (_marker->tempo())
2400 /* use the new marker for the grab */
2401 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2404 TempoMap& map (_editor->session()->tempo_map());
2405 /* get current state */
2406 before_state = &map.get_state();
2407 /* remove the section while we drag it */
2408 map.remove_tempo (section, true);
2412 framepos_t const pf = adjusted_current_frame (event);
2413 _marker->set_position (pf);
2414 show_verbose_cursor_time (pf);
2418 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2420 if (!movement_occurred) {
2421 if (was_double_click()) {
2422 _editor->edit_tempo_marker (*_marker);
2427 if (!_marker->tempo().movable()) {
2431 motion (event, false);
2433 TempoMap& map (_editor->session()->tempo_map());
2434 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2435 Timecode::BBT_Time when;
2437 map.bbt_time (beat_time, when);
2439 if (_copy == true) {
2440 _editor->begin_reversible_command (_("copy tempo mark"));
2441 XMLNode &before = map.get_state();
2442 map.add_tempo (_marker->tempo(), when);
2443 XMLNode &after = map.get_state();
2444 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2445 _editor->commit_reversible_command ();
2448 _editor->begin_reversible_command (_("move tempo mark"));
2449 /* we removed it before, so add it back now */
2450 map.add_tempo (_marker->tempo(), when);
2451 XMLNode &after = map.get_state();
2452 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2453 _editor->commit_reversible_command ();
2456 // delete the dummy marker we used for visual representation while moving.
2457 // a new visual marker will show up automatically.
2462 TempoMarkerDrag::aborted (bool moved)
2464 _marker->set_position (_marker->tempo().frame());
2466 TempoMap& map (_editor->session()->tempo_map());
2467 /* we removed it before, so add it back now */
2468 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2469 // delete the dummy marker we used for visual representation while moving.
2470 // a new visual marker will show up automatically.
2475 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2476 : Drag (e, &c.time_bar_canvas_item())
2480 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2483 /** Do all the things we do when dragging the playhead to make it look as though
2484 * we have located, without actually doing the locate (because that would cause
2485 * the diskstream buffers to be refilled, which is too slow).
2488 CursorDrag::fake_locate (framepos_t t)
2490 _editor->playhead_cursor->set_position (t);
2492 Session* s = _editor->session ();
2493 if (s->timecode_transmission_suspended ()) {
2494 framepos_t const f = _editor->playhead_cursor->current_frame ();
2495 /* This is asynchronous so it will be sent "now"
2497 s->send_mmc_locate (f);
2498 /* These are synchronous and will be sent during the next
2501 s->queue_full_time_code ();
2502 s->queue_song_position_pointer ();
2505 show_verbose_cursor_time (t);
2506 _editor->UpdateAllTransportClocks (t);
2510 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2512 Drag::start_grab (event, c);
2514 _grab_zoom = _editor->samples_per_pixel;
2516 framepos_t where = _editor->canvas_event_sample (event);
2518 _editor->snap_to_with_modifier (where, event);
2520 _editor->_dragging_playhead = true;
2522 Session* s = _editor->session ();
2524 /* grab the track canvas item as well */
2526 _cursor.track_canvas_item().grab();
2529 if (_was_rolling && _stop) {
2533 if (s->is_auditioning()) {
2534 s->cancel_audition ();
2538 if (AudioEngine::instance()->connected()) {
2540 /* do this only if we're the engine is connected
2541 * because otherwise this request will never be
2542 * serviced and we'll busy wait forever. likewise,
2543 * notice if we are disconnected while waiting for the
2544 * request to be serviced.
2547 s->request_suspend_timecode_transmission ();
2548 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2549 /* twiddle our thumbs */
2554 fake_locate (where);
2558 CursorDrag::motion (GdkEvent* event, bool)
2560 framepos_t const adjusted_frame = adjusted_current_frame (event);
2561 if (adjusted_frame != last_pointer_frame()) {
2562 fake_locate (adjusted_frame);
2567 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2569 _editor->_dragging_playhead = false;
2571 _cursor.track_canvas_item().ungrab();
2573 if (!movement_occurred && _stop) {
2577 motion (event, false);
2579 Session* s = _editor->session ();
2581 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2582 _editor->_pending_locate_request = true;
2583 s->request_resume_timecode_transmission ();
2588 CursorDrag::aborted (bool)
2590 _cursor.track_canvas_item().ungrab();
2592 if (_editor->_dragging_playhead) {
2593 _editor->session()->request_resume_timecode_transmission ();
2594 _editor->_dragging_playhead = false;
2597 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2600 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2601 : RegionDrag (e, i, p, v)
2603 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2607 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2609 Drag::start_grab (event, cursor);
2611 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2612 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2614 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2618 FadeInDrag::setup_pointer_frame_offset ()
2620 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2621 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2622 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2626 FadeInDrag::motion (GdkEvent* event, bool)
2628 framecnt_t fade_length;
2630 framepos_t const pos = adjusted_current_frame (event);
2632 boost::shared_ptr<Region> region = _primary->region ();
2634 if (pos < (region->position() + 64)) {
2635 fade_length = 64; // this should be a minimum defined somewhere
2636 } else if (pos > region->last_frame()) {
2637 fade_length = region->length();
2639 fade_length = pos - region->position();
2642 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2644 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2650 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2653 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2657 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2659 if (!movement_occurred) {
2663 framecnt_t fade_length;
2665 framepos_t const pos = adjusted_current_frame (event);
2667 boost::shared_ptr<Region> region = _primary->region ();
2669 if (pos < (region->position() + 64)) {
2670 fade_length = 64; // this should be a minimum defined somewhere
2671 } else if (pos > region->last_frame()) {
2672 fade_length = region->length();
2674 fade_length = pos - region->position();
2677 _editor->begin_reversible_command (_("change fade in length"));
2679 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2681 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2687 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2688 XMLNode &before = alist->get_state();
2690 tmp->audio_region()->set_fade_in_length (fade_length);
2691 tmp->audio_region()->set_fade_in_active (true);
2693 XMLNode &after = alist->get_state();
2694 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2697 _editor->commit_reversible_command ();
2701 FadeInDrag::aborted (bool)
2703 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2704 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2710 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2714 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2715 : RegionDrag (e, i, p, v)
2717 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2721 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2723 Drag::start_grab (event, cursor);
2725 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2726 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2728 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2732 FadeOutDrag::setup_pointer_frame_offset ()
2734 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2735 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2736 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2740 FadeOutDrag::motion (GdkEvent* event, bool)
2742 framecnt_t fade_length;
2744 framepos_t const pos = adjusted_current_frame (event);
2746 boost::shared_ptr<Region> region = _primary->region ();
2748 if (pos > (region->last_frame() - 64)) {
2749 fade_length = 64; // this should really be a minimum fade defined somewhere
2751 else if (pos < region->position()) {
2752 fade_length = region->length();
2755 fade_length = region->last_frame() - pos;
2758 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2760 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2766 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2769 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2773 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2775 if (!movement_occurred) {
2779 framecnt_t fade_length;
2781 framepos_t const pos = adjusted_current_frame (event);
2783 boost::shared_ptr<Region> region = _primary->region ();
2785 if (pos > (region->last_frame() - 64)) {
2786 fade_length = 64; // this should really be a minimum fade defined somewhere
2788 else if (pos < region->position()) {
2789 fade_length = region->length();
2792 fade_length = region->last_frame() - pos;
2795 _editor->begin_reversible_command (_("change fade out length"));
2797 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2799 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2805 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2806 XMLNode &before = alist->get_state();
2808 tmp->audio_region()->set_fade_out_length (fade_length);
2809 tmp->audio_region()->set_fade_out_active (true);
2811 XMLNode &after = alist->get_state();
2812 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2815 _editor->commit_reversible_command ();
2819 FadeOutDrag::aborted (bool)
2821 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2822 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2828 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2832 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2835 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2837 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2840 _points.push_back (ArdourCanvas::Duple (0, 0));
2841 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2844 MarkerDrag::~MarkerDrag ()
2846 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2851 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2853 location = new Location (*l);
2854 markers.push_back (m);
2859 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2861 Drag::start_grab (event, cursor);
2865 Location *location = _editor->find_location_from_marker (_marker, is_start);
2866 _editor->_dragging_edit_point = true;
2868 update_item (location);
2870 // _drag_line->show();
2871 // _line->raise_to_top();
2874 show_verbose_cursor_time (location->start());
2876 show_verbose_cursor_time (location->end());
2879 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2882 case Selection::Toggle:
2883 /* we toggle on the button release */
2885 case Selection::Set:
2886 if (!_editor->selection->selected (_marker)) {
2887 _editor->selection->set (_marker);
2890 case Selection::Extend:
2892 Locations::LocationList ll;
2893 list<Marker*> to_add;
2895 _editor->selection->markers.range (s, e);
2896 s = min (_marker->position(), s);
2897 e = max (_marker->position(), e);
2900 if (e < max_framepos) {
2903 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2904 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2905 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2908 to_add.push_back (lm->start);
2911 to_add.push_back (lm->end);
2915 if (!to_add.empty()) {
2916 _editor->selection->add (to_add);
2920 case Selection::Add:
2921 _editor->selection->add (_marker);
2925 /* Set up copies for us to manipulate during the drag
2928 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2930 Location* l = _editor->find_location_from_marker (*i, is_start);
2937 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2939 /* range: check that the other end of the range isn't
2942 CopiedLocationInfo::iterator x;
2943 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2944 if (*(*x).location == *l) {
2948 if (x == _copied_locations.end()) {
2949 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2951 (*x).markers.push_back (*i);
2952 (*x).move_both = true;
2960 MarkerDrag::setup_pointer_frame_offset ()
2963 Location *location = _editor->find_location_from_marker (_marker, is_start);
2964 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2968 MarkerDrag::motion (GdkEvent* event, bool)
2970 framecnt_t f_delta = 0;
2972 bool move_both = false;
2973 Location *real_location;
2974 Location *copy_location = 0;
2976 framepos_t const newframe = adjusted_current_frame (event);
2977 framepos_t next = newframe;
2979 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2983 CopiedLocationInfo::iterator x;
2985 /* find the marker we're dragging, and compute the delta */
2987 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2989 copy_location = (*x).location;
2991 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2993 /* this marker is represented by this
2994 * CopiedLocationMarkerInfo
2997 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3002 if (real_location->is_mark()) {
3003 f_delta = newframe - copy_location->start();
3007 switch (_marker->type()) {
3008 case Marker::SessionStart:
3009 case Marker::RangeStart:
3010 case Marker::LoopStart:
3011 case Marker::PunchIn:
3012 f_delta = newframe - copy_location->start();
3015 case Marker::SessionEnd:
3016 case Marker::RangeEnd:
3017 case Marker::LoopEnd:
3018 case Marker::PunchOut:
3019 f_delta = newframe - copy_location->end();
3022 /* what kind of marker is this ? */
3031 if (x == _copied_locations.end()) {
3032 /* hmm, impossible - we didn't find the dragged marker */
3036 /* now move them all */
3038 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3040 copy_location = x->location;
3042 /* call this to find out if its the start or end */
3044 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3048 if (real_location->locked()) {
3052 if (copy_location->is_mark()) {
3056 copy_location->set_start (copy_location->start() + f_delta);
3060 framepos_t new_start = copy_location->start() + f_delta;
3061 framepos_t new_end = copy_location->end() + f_delta;
3063 if (is_start) { // start-of-range marker
3065 if (move_both || (*x).move_both) {
3066 copy_location->set_start (new_start);
3067 copy_location->set_end (new_end);
3068 } else if (new_start < copy_location->end()) {
3069 copy_location->set_start (new_start);
3070 } else if (newframe > 0) {
3071 _editor->snap_to (next, 1, true);
3072 copy_location->set_end (next);
3073 copy_location->set_start (newframe);
3076 } else { // end marker
3078 if (move_both || (*x).move_both) {
3079 copy_location->set_end (new_end);
3080 copy_location->set_start (new_start);
3081 } else if (new_end > copy_location->start()) {
3082 copy_location->set_end (new_end);
3083 } else if (newframe > 0) {
3084 _editor->snap_to (next, -1, true);
3085 copy_location->set_start (next);
3086 copy_location->set_end (newframe);
3091 update_item (copy_location);
3093 /* now lookup the actual GUI items used to display this
3094 * location and move them to wherever the copy of the location
3095 * is now. This means that the logic in ARDOUR::Location is
3096 * still enforced, even though we are not (yet) modifying
3097 * the real Location itself.
3100 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3103 lm->set_position (copy_location->start(), copy_location->end());
3108 assert (!_copied_locations.empty());
3110 show_verbose_cursor_time (newframe);
3114 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3116 if (!movement_occurred) {
3118 if (was_double_click()) {
3119 _editor->rename_marker (_marker);
3123 /* just a click, do nothing but finish
3124 off the selection process
3127 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3130 case Selection::Set:
3131 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3132 _editor->selection->set (_marker);
3136 case Selection::Toggle:
3137 /* we toggle on the button release, click only */
3138 _editor->selection->toggle (_marker);
3141 case Selection::Extend:
3142 case Selection::Add:
3149 _editor->_dragging_edit_point = false;
3151 _editor->begin_reversible_command ( _("move marker") );
3152 XMLNode &before = _editor->session()->locations()->get_state();
3154 MarkerSelection::iterator i;
3155 CopiedLocationInfo::iterator x;
3158 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3159 x != _copied_locations.end() && i != _editor->selection->markers.end();
3162 Location * location = _editor->find_location_from_marker (*i, is_start);
3166 if (location->locked()) {
3170 if (location->is_mark()) {
3171 location->set_start (((*x).location)->start());
3173 location->set (((*x).location)->start(), ((*x).location)->end());
3178 XMLNode &after = _editor->session()->locations()->get_state();
3179 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3180 _editor->commit_reversible_command ();
3184 MarkerDrag::aborted (bool)
3190 MarkerDrag::update_item (Location*)
3195 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3197 _cumulative_x_drag (0),
3198 _cumulative_y_drag (0)
3200 if (_zero_gain_fraction < 0.0) {
3201 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3204 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3206 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3212 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3214 Drag::start_grab (event, _editor->cursors()->fader);
3216 // start the grab at the center of the control point so
3217 // the point doesn't 'jump' to the mouse after the first drag
3218 _fixed_grab_x = _point->get_x();
3219 _fixed_grab_y = _point->get_y();
3221 float const fraction = 1 - (_point->get_y() / _point->line().height());
3223 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3225 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3226 event->button.x + 10, event->button.y + 10);
3228 _editor->verbose_cursor()->show ();
3230 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3232 if (!_point->can_slide ()) {
3233 _x_constrained = true;
3238 ControlPointDrag::motion (GdkEvent* event, bool)
3240 double dx = _drags->current_pointer_x() - last_pointer_x();
3241 double dy = _drags->current_pointer_y() - last_pointer_y();
3243 if (event->button.state & Keyboard::SecondaryModifier) {
3248 /* coordinate in pixels relative to the start of the region (for region-based automation)
3249 or track (for track-based automation) */
3250 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3251 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3253 // calculate zero crossing point. back off by .01 to stay on the
3254 // positive side of zero
3255 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3257 // make sure we hit zero when passing through
3258 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3262 if (_x_constrained) {
3265 if (_y_constrained) {
3269 _cumulative_x_drag = cx - _fixed_grab_x;
3270 _cumulative_y_drag = cy - _fixed_grab_y;
3274 cy = min ((double) _point->line().height(), cy);
3276 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3278 if (!_x_constrained) {
3279 _editor->snap_to_with_modifier (cx_frames, event);
3282 cx_frames = min (cx_frames, _point->line().maximum_time());
3284 float const fraction = 1.0 - (cy / _point->line().height());
3286 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3288 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3292 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3294 if (!movement_occurred) {
3298 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3299 _editor->reset_point_selection ();
3303 motion (event, false);
3306 _point->line().end_drag (_pushing, _final_index);
3307 _editor->session()->commit_reversible_command ();
3311 ControlPointDrag::aborted (bool)
3313 _point->line().reset ();
3317 ControlPointDrag::active (Editing::MouseMode m)
3319 if (m == Editing::MouseGain) {
3320 /* always active in mouse gain */
3324 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3325 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3328 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3331 _cumulative_y_drag (0)
3333 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3337 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3339 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3342 _item = &_line->grab_item ();
3344 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3345 origin, and ditto for y.
3348 double cx = event->button.x;
3349 double cy = event->button.y;
3351 _line->parent_group().canvas_to_item (cx, cy);
3353 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3358 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3359 /* no adjacent points */
3363 Drag::start_grab (event, _editor->cursors()->fader);
3365 /* store grab start in parent frame */
3370 double fraction = 1.0 - (cy / _line->height());
3372 _line->start_drag_line (before, after, fraction);
3374 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3375 event->button.x + 10, event->button.y + 10);
3377 _editor->verbose_cursor()->show ();
3381 LineDrag::motion (GdkEvent* event, bool)
3383 double dy = _drags->current_pointer_y() - last_pointer_y();
3385 if (event->button.state & Keyboard::SecondaryModifier) {
3389 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3391 _cumulative_y_drag = cy - _fixed_grab_y;
3394 cy = min ((double) _line->height(), cy);
3396 double const fraction = 1.0 - (cy / _line->height());
3399 /* we are ignoring x position for this drag, so we can just pass in anything */
3400 _line->drag_motion (0, fraction, true, false, ignored);
3402 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3406 LineDrag::finished (GdkEvent* event, bool movement_occured)
3408 if (movement_occured) {
3409 motion (event, false);
3410 _line->end_drag (false, 0);
3412 /* add a new control point on the line */
3414 AutomationTimeAxisView* atv;
3416 _line->end_drag (false, 0);
3418 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3419 framepos_t where = _editor->window_event_sample (event, 0, 0);
3420 atv->add_automation_event (event, where, event->button.y, false);
3424 _editor->session()->commit_reversible_command ();
3428 LineDrag::aborted (bool)
3433 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3436 _cumulative_x_drag (0)
3438 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3442 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3444 Drag::start_grab (event);
3446 _line = reinterpret_cast<Line*> (_item);
3449 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3451 double cx = event->button.x;
3452 double cy = event->button.y;
3454 _item->parent()->canvas_to_item (cx, cy);
3456 /* store grab start in parent frame */
3457 _region_view_grab_x = cx;
3459 _before = *(float*) _item->get_data ("position");
3461 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3463 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3467 FeatureLineDrag::motion (GdkEvent*, bool)
3469 double dx = _drags->current_pointer_x() - last_pointer_x();
3471 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3473 _cumulative_x_drag += dx;
3475 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3484 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3486 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3488 float *pos = new float;
3491 _line->set_data ("position", pos);
3497 FeatureLineDrag::finished (GdkEvent*, bool)
3499 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3500 _arv->update_transient(_before, _before);
3504 FeatureLineDrag::aborted (bool)
3509 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3511 , _vertical_only (false)
3513 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3517 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3519 Drag::start_grab (event);
3520 show_verbose_cursor_time (adjusted_current_frame (event));
3524 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3531 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3533 framepos_t grab = grab_frame ();
3534 if (Config->get_rubberbanding_snaps_to_grid ()) {
3535 _editor->snap_to_with_modifier (grab, event);
3538 /* base start and end on initial click position */
3548 if (_drags->current_pointer_y() < grab_y()) {
3549 y1 = _drags->current_pointer_y();
3552 y2 = _drags->current_pointer_y();
3557 if (start != end || y1 != y2) {
3559 double x1 = _editor->sample_to_pixel (start);
3560 double x2 = _editor->sample_to_pixel (end);
3561 const double min_dimension = 2.0;
3563 _editor->rubberband_rect->set_x0 (x1);
3564 if (_vertical_only) {
3565 /* fixed 10 pixel width */
3566 _editor->rubberband_rect->set_x1 (x1 + 10);
3569 x2 = min (x1 - min_dimension, x2);
3571 x2 = max (x1 + min_dimension, x2);
3573 _editor->rubberband_rect->set_x1 (x2);
3576 _editor->rubberband_rect->set_y0 (y1);
3578 y2 = min (y1 - min_dimension, y2);
3580 y2 = max (y1 + min_dimension, y2);
3583 _editor->rubberband_rect->set_y1 (y2);
3585 _editor->rubberband_rect->show();
3586 _editor->rubberband_rect->raise_to_top();
3588 show_verbose_cursor_time (pf);
3590 do_select_things (event, true);
3595 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3600 if (grab_frame() < last_pointer_frame()) {
3602 x2 = last_pointer_frame ();
3605 x1 = last_pointer_frame ();
3611 if (_drags->current_pointer_y() < grab_y()) {
3612 y1 = _drags->current_pointer_y();
3615 y2 = _drags->current_pointer_y();
3619 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3623 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3625 if (movement_occurred) {
3627 motion (event, false);
3628 do_select_things (event, false);
3634 bool do_deselect = true;
3635 MidiTimeAxisView* mtv;
3637 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3639 if (_editor->selection->empty()) {
3640 /* nothing selected */
3641 add_midi_region (mtv);
3642 do_deselect = false;
3646 /* do not deselect if Primary or Tertiary (toggle-select or
3647 * extend-select are pressed.
3650 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3651 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3658 _editor->rubberband_rect->hide();
3662 RubberbandSelectDrag::aborted (bool)
3664 _editor->rubberband_rect->hide ();
3667 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3668 : RegionDrag (e, i, p, v)
3670 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3674 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3676 Drag::start_grab (event, cursor);
3678 show_verbose_cursor_time (adjusted_current_frame (event));
3682 TimeFXDrag::motion (GdkEvent* event, bool)
3684 RegionView* rv = _primary;
3685 StreamView* cv = rv->get_time_axis_view().view ();
3687 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3688 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3689 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3691 framepos_t const pf = adjusted_current_frame (event);
3693 if (pf > rv->region()->position()) {
3694 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3697 show_verbose_cursor_time (pf);
3701 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3703 _primary->get_time_axis_view().hide_timestretch ();
3705 if (!movement_occurred) {
3709 if (last_pointer_frame() < _primary->region()->position()) {
3710 /* backwards drag of the left edge - not usable */
3714 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3716 float percentage = (double) newlen / (double) _primary->region()->length();
3718 #ifndef USE_RUBBERBAND
3719 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3720 if (_primary->region()->data_type() == DataType::AUDIO) {
3721 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3725 if (!_editor->get_selection().regions.empty()) {
3726 /* primary will already be included in the selection, and edit
3727 group shared editing will propagate selection across
3728 equivalent regions, so just use the current region
3732 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3733 error << _("An error occurred while executing time stretch operation") << endmsg;
3739 TimeFXDrag::aborted (bool)
3741 _primary->get_time_axis_view().hide_timestretch ();
3744 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3747 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3751 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3753 Drag::start_grab (event);
3757 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3759 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3763 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3765 if (movement_occurred && _editor->session()) {
3766 /* make sure we stop */
3767 _editor->session()->request_transport_speed (0.0);
3772 ScrubDrag::aborted (bool)
3777 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3782 , _original_pointer_time_axis (-1)
3783 , _last_pointer_time_axis (-1)
3784 , _time_selection_at_start (!_editor->get_selection().time.empty())
3786 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3788 if (_time_selection_at_start) {
3789 start_at_start = _editor->get_selection().time.start();
3790 end_at_start = _editor->get_selection().time.end_frame();
3795 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3797 if (_editor->session() == 0) {
3801 Gdk::Cursor* cursor = 0;
3803 switch (_operation) {
3804 case CreateSelection:
3805 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3810 cursor = _editor->cursors()->selector;
3811 Drag::start_grab (event, cursor);
3814 case SelectionStartTrim:
3815 if (_editor->clicked_axisview) {
3816 _editor->clicked_axisview->order_selection_trims (_item, true);
3818 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3821 case SelectionEndTrim:
3822 if (_editor->clicked_axisview) {
3823 _editor->clicked_axisview->order_selection_trims (_item, false);
3825 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3829 Drag::start_grab (event, cursor);
3832 case SelectionExtend:
3833 Drag::start_grab (event, cursor);
3837 if (_operation == SelectionMove) {
3838 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3840 show_verbose_cursor_time (adjusted_current_frame (event));
3843 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3847 SelectionDrag::setup_pointer_frame_offset ()
3849 switch (_operation) {
3850 case CreateSelection:
3851 _pointer_frame_offset = 0;
3854 case SelectionStartTrim:
3856 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3859 case SelectionEndTrim:
3860 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3863 case SelectionExtend:
3869 SelectionDrag::motion (GdkEvent* event, bool first_move)
3871 framepos_t start = 0;
3873 framecnt_t length = 0;
3874 framecnt_t distance = 0;
3876 framepos_t const pending_position = adjusted_current_frame (event);
3878 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3882 switch (_operation) {
3883 case CreateSelection:
3885 framepos_t grab = grab_frame ();
3888 grab = adjusted_current_frame (event, false);
3889 if (grab < pending_position) {
3890 _editor->snap_to (grab, -1);
3892 _editor->snap_to (grab, 1);
3896 if (pending_position < grab) {
3897 start = pending_position;
3900 end = pending_position;
3904 /* first drag: Either add to the selection
3905 or create a new selection
3912 /* adding to the selection */
3913 _editor->set_selected_track_as_side_effect (Selection::Add);
3914 _editor->clicked_selection = _editor->selection->add (start, end);
3921 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3922 _editor->set_selected_track_as_side_effect (Selection::Set);
3925 _editor->clicked_selection = _editor->selection->set (start, end);
3929 /* select all tracks within the rectangle that we've marked out so far */
3930 TrackViewList to_be_added_to_selection;
3931 TrackViewList to_be_removed_from_selection;
3932 TrackViewList& all_tracks (_editor->track_views);
3934 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3936 if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
3937 if (!(*i)->get_selected()) {
3938 to_be_added_to_selection.push_back (*i);
3941 if ((*i)->get_selected()) {
3942 to_be_removed_from_selection.push_back (*i);
3947 if (!to_be_added_to_selection.empty()) {
3948 _editor->selection->add (to_be_added_to_selection);
3951 if (!to_be_removed_from_selection.empty()) {
3952 _editor->selection->remove (to_be_removed_from_selection);
3957 case SelectionStartTrim:
3959 start = _editor->selection->time[_editor->clicked_selection].start;
3960 end = _editor->selection->time[_editor->clicked_selection].end;
3962 if (pending_position > end) {
3965 start = pending_position;
3969 case SelectionEndTrim:
3971 start = _editor->selection->time[_editor->clicked_selection].start;
3972 end = _editor->selection->time[_editor->clicked_selection].end;
3974 if (pending_position < start) {
3977 end = pending_position;
3984 start = _editor->selection->time[_editor->clicked_selection].start;
3985 end = _editor->selection->time[_editor->clicked_selection].end;
3987 length = end - start;
3988 distance = pending_position - start;
3989 start = pending_position;
3990 _editor->snap_to (start);
3992 end = start + length;
3996 case SelectionExtend:
4000 _editor->maybe_autoscroll (true, false, false);
4003 switch (_operation) {
4005 if (_time_selection_at_start) {
4006 _editor->selection->move_time (distance);
4010 _editor->selection->replace (_editor->clicked_selection, start, end);
4014 if (_operation == SelectionMove) {
4015 show_verbose_cursor_time(start);
4017 show_verbose_cursor_time(pending_position);
4022 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4024 Session* s = _editor->session();
4026 if (movement_occurred) {
4027 motion (event, false);
4028 /* XXX this is not object-oriented programming at all. ick */
4029 if (_editor->selection->time.consolidate()) {
4030 _editor->selection->TimeChanged ();
4033 /* XXX what if its a music time selection? */
4035 if ( s->get_play_range() && s->transport_rolling() ) {
4036 s->request_play_range (&_editor->selection->time, true);
4038 if (Config->get_always_play_range() && !s->transport_rolling()) {
4039 s->request_locate (_editor->get_selection().time.start());
4045 /* just a click, no pointer movement.
4048 if (_operation == SelectionExtend) {
4049 if (_time_selection_at_start) {
4050 framepos_t pos = adjusted_current_frame (event, false);
4051 framepos_t start = min (pos, start_at_start);
4052 framepos_t end = max (pos, end_at_start);
4053 _editor->selection->set (start, end);
4056 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4057 if (_editor->clicked_selection) {
4058 _editor->selection->remove (_editor->clicked_selection);
4061 if (!_editor->clicked_selection) {
4062 _editor->selection->clear_time();
4067 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4068 _editor->selection->set (_editor->clicked_axisview);
4071 if (s && s->get_play_range () && s->transport_rolling()) {
4072 s->request_stop (false, false);
4077 _editor->stop_canvas_autoscroll ();
4078 _editor->clicked_selection = 0;
4082 SelectionDrag::aborted (bool)
4087 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4092 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4094 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4095 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4096 physical_screen_height (_editor->get_window())));
4097 _drag_rect->hide ();
4099 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4100 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4104 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4106 if (_editor->session() == 0) {
4110 Gdk::Cursor* cursor = 0;
4112 if (!_editor->temp_location) {
4113 _editor->temp_location = new Location (*_editor->session());
4116 switch (_operation) {
4117 case CreateRangeMarker:
4118 case CreateTransportMarker:
4119 case CreateCDMarker:
4121 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4126 cursor = _editor->cursors()->selector;
4130 Drag::start_grab (event, cursor);
4132 show_verbose_cursor_time (adjusted_current_frame (event));
4136 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4138 framepos_t start = 0;
4140 ArdourCanvas::Rectangle *crect;
4142 switch (_operation) {
4143 case CreateRangeMarker:
4144 crect = _editor->range_bar_drag_rect;
4146 case CreateTransportMarker:
4147 crect = _editor->transport_bar_drag_rect;
4149 case CreateCDMarker:
4150 crect = _editor->cd_marker_bar_drag_rect;
4153 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4158 framepos_t const pf = adjusted_current_frame (event);
4160 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4161 framepos_t grab = grab_frame ();
4162 _editor->snap_to (grab);
4164 if (pf < grab_frame()) {
4172 /* first drag: Either add to the selection
4173 or create a new selection.
4178 _editor->temp_location->set (start, end);
4182 update_item (_editor->temp_location);
4184 //_drag_rect->raise_to_top();
4189 _editor->maybe_autoscroll (true, false, false);
4192 _editor->temp_location->set (start, end);
4194 double x1 = _editor->sample_to_pixel (start);
4195 double x2 = _editor->sample_to_pixel (end);
4199 update_item (_editor->temp_location);
4202 show_verbose_cursor_time (pf);
4207 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4209 Location * newloc = 0;
4213 if (movement_occurred) {
4214 motion (event, false);
4217 switch (_operation) {
4218 case CreateRangeMarker:
4219 case CreateCDMarker:
4221 _editor->begin_reversible_command (_("new range marker"));
4222 XMLNode &before = _editor->session()->locations()->get_state();
4223 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4224 if (_operation == CreateCDMarker) {
4225 flags = Location::IsRangeMarker | Location::IsCDMarker;
4226 _editor->cd_marker_bar_drag_rect->hide();
4229 flags = Location::IsRangeMarker;
4230 _editor->range_bar_drag_rect->hide();
4232 newloc = new Location (
4233 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4236 _editor->session()->locations()->add (newloc, true);
4237 XMLNode &after = _editor->session()->locations()->get_state();
4238 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4239 _editor->commit_reversible_command ();
4243 case CreateTransportMarker:
4244 // popup menu to pick loop or punch
4245 _editor->new_transport_marker_context_menu (&event->button, _item);
4251 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4253 if (_operation == CreateTransportMarker) {
4255 /* didn't drag, so just locate */
4257 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4259 } else if (_operation == CreateCDMarker) {
4261 /* didn't drag, but mark is already created so do
4264 } else { /* operation == CreateRangeMarker */
4270 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4272 if (end == max_framepos) {
4273 end = _editor->session()->current_end_frame ();
4276 if (start == max_framepos) {
4277 start = _editor->session()->current_start_frame ();
4280 switch (_editor->mouse_mode) {
4282 /* find the two markers on either side and then make the selection from it */
4283 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4287 /* find the two markers on either side of the click and make the range out of it */
4288 _editor->selection->set (start, end);
4297 _editor->stop_canvas_autoscroll ();
4301 RangeMarkerBarDrag::aborted (bool)
4307 RangeMarkerBarDrag::update_item (Location* location)
4309 double const x1 = _editor->sample_to_pixel (location->start());
4310 double const x2 = _editor->sample_to_pixel (location->end());
4312 _drag_rect->set_x0 (x1);
4313 _drag_rect->set_x1 (x2);
4316 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4320 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4324 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4326 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4327 Drag::start_grab (event, _editor->cursors()->zoom_out);
4330 Drag::start_grab (event, _editor->cursors()->zoom_in);
4334 show_verbose_cursor_time (adjusted_current_frame (event));
4338 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4343 framepos_t const pf = adjusted_current_frame (event);
4345 framepos_t grab = grab_frame ();
4346 _editor->snap_to_with_modifier (grab, event);
4348 /* base start and end on initial click position */
4360 _editor->zoom_rect->show();
4361 _editor->zoom_rect->raise_to_top();
4364 _editor->reposition_zoom_rect(start, end);
4366 show_verbose_cursor_time (pf);
4371 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4373 if (movement_occurred) {
4374 motion (event, false);
4376 if (grab_frame() < last_pointer_frame()) {
4377 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4379 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4382 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4383 _editor->tav_zoom_step (_zoom_out);
4385 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4389 _editor->zoom_rect->hide();
4393 MouseZoomDrag::aborted (bool)
4395 _editor->zoom_rect->hide ();
4398 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4400 , _cumulative_dx (0)
4401 , _cumulative_dy (0)
4403 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4405 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4407 _region = &_primary->region_view ();
4408 _note_height = _region->midi_stream_view()->note_height ();
4412 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4414 Drag::start_grab (event);
4416 if (!(_was_selected = _primary->selected())) {
4418 /* tertiary-click means extend selection - we'll do that on button release,
4419 so don't add it here, because otherwise we make it hard to figure
4420 out the "extend-to" range.
4423 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4426 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4429 _region->note_selected (_primary, true);
4431 _region->unique_select (_primary);
4437 /** @return Current total drag x change in frames */
4439 NoteDrag::total_dx () const
4442 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4444 /* primary note time */
4445 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4447 /* new time of the primary note in session frames */
4448 frameoffset_t st = n + dx;
4450 framepos_t const rp = _region->region()->position ();
4452 /* prevent the note being dragged earlier than the region's position */
4455 /* snap and return corresponding delta */
4456 return _region->snap_frame_to_frame (st - rp) + rp - n;
4459 /** @return Current total drag y change in note number */
4461 NoteDrag::total_dy () const
4463 MidiStreamView* msv = _region->midi_stream_view ();
4464 double const y = _region->midi_view()->y_position ();
4465 /* new current note */
4466 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4468 n = max (msv->lowest_note(), n);
4469 n = min (msv->highest_note(), n);
4470 /* and work out delta */
4471 return n - msv->y_to_note (grab_y() - y);
4475 NoteDrag::motion (GdkEvent *, bool)
4477 /* Total change in x and y since the start of the drag */
4478 frameoffset_t const dx = total_dx ();
4479 int8_t const dy = total_dy ();
4481 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4482 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4483 double const tdy = -dy * _note_height - _cumulative_dy;
4486 _cumulative_dx += tdx;
4487 _cumulative_dy += tdy;
4489 int8_t note_delta = total_dy();
4491 _region->move_selection (tdx, tdy, note_delta);
4493 /* the new note value may be the same as the old one, but we
4494 * don't know what that means because the selection may have
4495 * involved more than one note and we might be doing something
4496 * odd with them. so show the note value anyway, always.
4500 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4502 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4503 (int) floor ((double)new_note));
4505 show_verbose_cursor_text (buf);
4510 NoteDrag::finished (GdkEvent* ev, bool moved)
4513 /* no motion - select note */
4515 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4516 _editor->current_mouse_mode() == Editing::MouseDraw) {
4518 if (_was_selected) {
4519 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4521 _region->note_deselected (_primary);
4524 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4525 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4527 if (!extend && !add && _region->selection_size() > 1) {
4528 _region->unique_select (_primary);
4529 } else if (extend) {
4530 _region->note_selected (_primary, true, true);
4532 /* it was added during button press */
4537 _region->note_dropped (_primary, total_dx(), total_dy());
4542 NoteDrag::aborted (bool)
4547 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4548 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4549 : Drag (editor, atv->base_item ())
4551 , _nothing_to_drag (false)
4553 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4554 y_origin = atv->y_position();
4555 setup (atv->lines ());
4558 /** Make an AutomationRangeDrag for region gain lines */
4559 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4560 : Drag (editor, rv->get_canvas_group ())
4562 , _nothing_to_drag (false)
4564 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4566 list<boost::shared_ptr<AutomationLine> > lines;
4567 lines.push_back (rv->get_gain_line ());
4568 y_origin = rv->get_time_axis_view().y_position();
4572 /** @param lines AutomationLines to drag.
4573 * @param offset Offset from the session start to the points in the AutomationLines.
4576 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4578 /* find the lines that overlap the ranges being dragged */
4579 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4580 while (i != lines.end ()) {
4581 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4584 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4586 /* check this range against all the AudioRanges that we are using */
4587 list<AudioRange>::const_iterator k = _ranges.begin ();
4588 while (k != _ranges.end()) {
4589 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4595 /* add it to our list if it overlaps at all */
4596 if (k != _ranges.end()) {
4601 _lines.push_back (n);
4607 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4611 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4613 return 1.0 - ((global_y - y_origin) / line->height());
4617 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4619 Drag::start_grab (event, cursor);
4621 /* Get line states before we start changing things */
4622 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4623 i->state = &i->line->get_state ();
4624 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4627 if (_ranges.empty()) {
4629 /* No selected time ranges: drag all points */
4630 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4631 uint32_t const N = i->line->npoints ();
4632 for (uint32_t j = 0; j < N; ++j) {
4633 i->points.push_back (i->line->nth (j));
4639 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4641 framecnt_t const half = (i->start + i->end) / 2;
4643 /* find the line that this audio range starts in */
4644 list<Line>::iterator j = _lines.begin();
4645 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4649 if (j != _lines.end()) {
4650 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4652 /* j is the line that this audio range starts in; fade into it;
4653 64 samples length plucked out of thin air.
4656 framepos_t a = i->start + 64;
4661 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4662 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4664 the_list->add (p, the_list->eval (p));
4665 the_list->add (q, the_list->eval (q));
4668 /* same thing for the end */
4671 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4675 if (j != _lines.end()) {
4676 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4678 /* j is the line that this audio range starts in; fade out of it;
4679 64 samples length plucked out of thin air.
4682 framepos_t b = i->end - 64;
4687 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4688 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4690 the_list->add (p, the_list->eval (p));
4691 the_list->add (q, the_list->eval (q));
4695 _nothing_to_drag = true;
4697 /* Find all the points that should be dragged and put them in the relevant
4698 points lists in the Line structs.
4701 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4703 uint32_t const N = i->line->npoints ();
4704 for (uint32_t j = 0; j < N; ++j) {
4706 /* here's a control point on this line */
4707 ControlPoint* p = i->line->nth (j);
4708 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4710 /* see if it's inside a range */
4711 list<AudioRange>::const_iterator k = _ranges.begin ();
4712 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4716 if (k != _ranges.end()) {
4717 /* dragging this point */
4718 _nothing_to_drag = false;
4719 i->points.push_back (p);
4725 if (_nothing_to_drag) {
4729 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4730 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4735 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4737 if (_nothing_to_drag) {
4741 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4742 float const f = y_fraction (l->line, _drags->current_pointer_y());
4743 /* we are ignoring x position for this drag, so we can just pass in anything */
4745 l->line->drag_motion (0, f, true, false, ignored);
4746 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4751 AutomationRangeDrag::finished (GdkEvent* event, bool)
4753 if (_nothing_to_drag) {
4757 motion (event, false);
4758 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4759 i->line->end_drag (false, 0);
4762 _editor->session()->commit_reversible_command ();
4766 AutomationRangeDrag::aborted (bool)
4768 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4773 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4776 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4777 layer = v->region()->layer ();
4778 initial_y = v->get_canvas_group()->position().y;
4779 initial_playlist = v->region()->playlist ();
4780 initial_position = v->region()->position ();
4781 initial_end = v->region()->position () + v->region()->length ();
4784 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4785 : Drag (e, i->canvas_item ())
4788 , _cumulative_dx (0)
4790 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4791 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4796 PatchChangeDrag::motion (GdkEvent* ev, bool)
4798 framepos_t f = adjusted_current_frame (ev);
4799 boost::shared_ptr<Region> r = _region_view->region ();
4800 f = max (f, r->position ());
4801 f = min (f, r->last_frame ());
4803 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4804 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4805 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4806 _cumulative_dx = dxu;
4810 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4812 if (!movement_occurred) {
4816 boost::shared_ptr<Region> r (_region_view->region ());
4817 framepos_t f = adjusted_current_frame (ev);
4818 f = max (f, r->position ());
4819 f = min (f, r->last_frame ());
4821 _region_view->move_patch_change (
4823 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4828 PatchChangeDrag::aborted (bool)
4830 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4834 PatchChangeDrag::setup_pointer_frame_offset ()
4836 boost::shared_ptr<Region> region = _region_view->region ();
4837 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4840 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4841 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4848 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4850 framepos_t const p = _region_view->region()->position ();
4851 double const y = _region_view->midi_view()->y_position ();
4853 x1 = max ((framepos_t) 0, x1 - p);
4854 x2 = max ((framepos_t) 0, x2 - p);
4855 y1 = max (0.0, y1 - y);
4856 y2 = max (0.0, y2 - y);
4858 _region_view->update_drag_selection (
4859 _editor->sample_to_pixel (x1),
4860 _editor->sample_to_pixel (x2),
4863 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4868 MidiRubberbandSelectDrag::deselect_things ()
4873 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4874 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4877 _vertical_only = true;
4881 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4883 double const y = _region_view->midi_view()->y_position ();
4885 y1 = max (0.0, y1 - y);
4886 y2 = max (0.0, y2 - y);
4888 _region_view->update_vertical_drag_selection (
4891 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4896 MidiVerticalSelectDrag::deselect_things ()
4901 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4902 : RubberbandSelectDrag (e, i)
4908 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4910 if (drag_in_progress) {
4911 /* We just want to select things at the end of the drag, not during it */
4915 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4917 _editor->begin_reversible_command (_("rubberband selection"));
4918 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4919 _editor->commit_reversible_command ();
4923 EditorRubberbandSelectDrag::deselect_things ()
4925 if (!getenv("ARDOUR_SAE")) {
4926 _editor->selection->clear_tracks();
4928 _editor->selection->clear_regions();
4929 _editor->selection->clear_points ();
4930 _editor->selection->clear_lines ();
4933 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4941 NoteCreateDrag::~NoteCreateDrag ()
4947 NoteCreateDrag::grid_frames (framepos_t t) const
4950 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4955 return _region_view->region_beats_to_region_frames (grid_beats);
4959 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4961 Drag::start_grab (event, cursor);
4963 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4965 framepos_t pf = _drags->current_pointer_frame ();
4966 framecnt_t const g = grid_frames (pf);
4968 /* Hack so that we always snap to the note that we are over, instead of snapping
4969 to the next one if we're more than halfway through the one we're over.
4971 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4975 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4977 MidiStreamView* sv = _region_view->midi_stream_view ();
4978 double const x = _editor->sample_to_pixel (_note[0]);
4979 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4981 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4982 _drag_rect->set_outline_all ();
4983 _drag_rect->set_outline_color (0xffffff99);
4984 _drag_rect->set_fill_color (0xffffff66);
4988 NoteCreateDrag::motion (GdkEvent* event, bool)
4990 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4991 double const x = _editor->sample_to_pixel (_note[1]);
4992 if (_note[1] > _note[0]) {
4993 _drag_rect->set_x1 (x);
4995 _drag_rect->set_x0 (x);
5000 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5002 if (!had_movement) {
5006 framepos_t const start = min (_note[0], _note[1]);
5007 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5009 framecnt_t const g = grid_frames (start);
5010 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5012 if (_editor->snap_mode() == SnapNormal && length < g) {
5013 length = g - one_tick;
5016 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5018 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5022 NoteCreateDrag::y_to_region (double y) const
5025 _region_view->get_canvas_group()->canvas_to_item (x, y);
5030 NoteCreateDrag::aborted (bool)
5035 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5040 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5044 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5046 Drag::start_grab (event, cursor);
5050 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5056 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5059 distance = _drags->current_pointer_x() - grab_x();
5060 len = ar->fade_in()->back()->when;
5062 distance = grab_x() - _drags->current_pointer_x();
5063 len = ar->fade_out()->back()->when;
5066 /* how long should it be ? */
5068 new_length = len + _editor->pixel_to_sample (distance);
5070 /* now check with the region that this is legal */
5072 new_length = ar->verify_xfade_bounds (new_length, start);
5075 arv->reset_fade_in_shape_width (ar, new_length);
5077 arv->reset_fade_out_shape_width (ar, new_length);
5082 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5088 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5091 distance = _drags->current_pointer_x() - grab_x();
5092 len = ar->fade_in()->back()->when;
5094 distance = grab_x() - _drags->current_pointer_x();
5095 len = ar->fade_out()->back()->when;
5098 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5100 _editor->begin_reversible_command ("xfade trim");
5101 ar->playlist()->clear_owned_changes ();
5104 ar->set_fade_in_length (new_length);
5106 ar->set_fade_out_length (new_length);
5109 /* Adjusting the xfade may affect other regions in the playlist, so we need
5110 to get undo Commands from the whole playlist rather than just the
5114 vector<Command*> cmds;
5115 ar->playlist()->rdiff (cmds);
5116 _editor->session()->add_commands (cmds);
5117 _editor->commit_reversible_command ();
5122 CrossfadeEdgeDrag::aborted (bool)
5125 arv->redraw_start_xfade ();
5127 arv->redraw_end_xfade ();