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 double new_length = len - diff;
2019 i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2020 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2027 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2028 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2029 if (changed && _preserve_fade_anchor) {
2030 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2032 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2033 framecnt_t len = ar->fade_out()->back()->when;
2034 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2035 double new_length = len + diff;
2036 i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2037 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2045 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2047 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2048 i->view->move_contents (frame_delta);
2054 switch (_operation) {
2056 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2059 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2062 // show_verbose_cursor_time (frame_delta);
2069 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2071 if (movement_occurred) {
2072 motion (event, false);
2074 if (_operation == StartTrim) {
2075 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2077 /* This must happen before the region's StatefulDiffCommand is created, as it may
2078 `correct' (ahem) the region's _start from being negative to being zero. It
2079 needs to be zero in the undo record.
2081 i->view->trim_front_ending ();
2083 if (_preserve_fade_anchor) {
2084 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2086 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2087 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2088 ar->set_fade_in_length(i->anchored_fade_length);
2089 ar->set_fade_in_active(true);
2092 if (_jump_position_when_done) {
2093 i->view->region()->set_position (i->initial_position);
2096 } else if (_operation == EndTrim) {
2097 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2098 if (_preserve_fade_anchor) {
2099 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2101 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2102 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2103 ar->set_fade_out_length(i->anchored_fade_length);
2104 ar->set_fade_out_active(true);
2107 if (_jump_position_when_done) {
2108 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2113 if (!_views.empty()) {
2114 if (_operation == StartTrim) {
2115 _editor->maybe_locate_with_edit_preroll(
2116 _views.begin()->view->region()->position());
2118 if (_operation == EndTrim) {
2119 _editor->maybe_locate_with_edit_preroll(
2120 _views.begin()->view->region()->position() +
2121 _views.begin()->view->region()->length());
2125 if (!_editor->selection->selected (_primary)) {
2126 _primary->thaw_after_trim ();
2129 set<boost::shared_ptr<Playlist> > diffed_playlists;
2131 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2132 i->view->thaw_after_trim ();
2133 i->view->enable_display (true);
2134 i->view->fake_set_opaque (true);
2136 /* Trimming one region may affect others on the playlist, so we need
2137 to get undo Commands from the whole playlist rather than just the
2138 region. Use diffed_playlists to make sure we don't diff a given
2139 playlist more than once.
2141 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2142 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2143 vector<Command*> cmds;
2145 _editor->session()->add_commands (cmds);
2146 diffed_playlists.insert (p);
2151 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2155 _editor->motion_frozen_playlists.clear ();
2156 _editor->commit_reversible_command();
2159 /* no mouse movement */
2160 _editor->point_trim (event, adjusted_current_frame (event));
2163 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2164 if (_operation == StartTrim) {
2165 i->view->trim_front_ending ();
2168 i->view->region()->resume_property_changes ();
2173 TrimDrag::aborted (bool movement_occurred)
2175 /* Our motion method is changing model state, so use the Undo system
2176 to cancel. Perhaps not ideal, as this will leave an Undo point
2177 behind which may be slightly odd from the user's point of view.
2182 if (movement_occurred) {
2186 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2187 i->view->region()->resume_property_changes ();
2192 TrimDrag::setup_pointer_frame_offset ()
2194 list<DraggingView>::iterator i = _views.begin ();
2195 while (i != _views.end() && i->view != _primary) {
2199 if (i == _views.end()) {
2203 switch (_operation) {
2205 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2208 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2215 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2219 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2220 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2225 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2227 Drag::start_grab (event, cursor);
2228 show_verbose_cursor_time (adjusted_current_frame(event));
2232 MeterMarkerDrag::setup_pointer_frame_offset ()
2234 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2238 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2240 if (!_marker->meter().movable()) {
2246 // create a dummy marker for visual representation of moving the
2247 // section, because whether its a copy or not, we're going to
2248 // leave or lose the original marker (leave if its a copy; lose if its
2249 // not, because we'll remove it from the map).
2251 MeterSection section (_marker->meter());
2253 if (!section.movable()) {
2258 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2260 _marker = new MeterMarker (
2262 *_editor->meter_group,
2263 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2265 *new MeterSection (_marker->meter())
2268 /* use the new marker for the grab */
2269 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2272 TempoMap& map (_editor->session()->tempo_map());
2273 /* get current state */
2274 before_state = &map.get_state();
2275 /* remove the section while we drag it */
2276 map.remove_meter (section, true);
2280 framepos_t const pf = adjusted_current_frame (event);
2281 _marker->set_position (pf);
2282 show_verbose_cursor_time (pf);
2286 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2288 if (!movement_occurred) {
2289 if (was_double_click()) {
2290 _editor->edit_meter_marker (*_marker);
2295 if (!_marker->meter().movable()) {
2299 motion (event, false);
2301 Timecode::BBT_Time when;
2303 TempoMap& map (_editor->session()->tempo_map());
2304 map.bbt_time (last_pointer_frame(), when);
2306 if (_copy == true) {
2307 _editor->begin_reversible_command (_("copy meter mark"));
2308 XMLNode &before = map.get_state();
2309 map.add_meter (_marker->meter(), when);
2310 XMLNode &after = map.get_state();
2311 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2312 _editor->commit_reversible_command ();
2315 _editor->begin_reversible_command (_("move meter mark"));
2317 /* we removed it before, so add it back now */
2319 map.add_meter (_marker->meter(), when);
2320 XMLNode &after = map.get_state();
2321 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2322 _editor->commit_reversible_command ();
2325 // delete the dummy marker we used for visual representation while moving.
2326 // a new visual marker will show up automatically.
2331 MeterMarkerDrag::aborted (bool moved)
2333 _marker->set_position (_marker->meter().frame ());
2336 TempoMap& map (_editor->session()->tempo_map());
2337 /* we removed it before, so add it back now */
2338 map.add_meter (_marker->meter(), _marker->meter().frame());
2339 // delete the dummy marker we used for visual representation while moving.
2340 // a new visual marker will show up automatically.
2345 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2349 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2351 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2356 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2358 Drag::start_grab (event, cursor);
2359 show_verbose_cursor_time (adjusted_current_frame (event));
2363 TempoMarkerDrag::setup_pointer_frame_offset ()
2365 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2369 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2371 if (!_marker->tempo().movable()) {
2377 // create a dummy marker for visual representation of moving the
2378 // section, because whether its a copy or not, we're going to
2379 // leave or lose the original marker (leave if its a copy; lose if its
2380 // not, because we'll remove it from the map).
2382 // create a dummy marker for visual representation of moving the copy.
2383 // The actual copying is not done before we reach the finish callback.
2386 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2388 TempoSection section (_marker->tempo());
2390 _marker = new TempoMarker (
2392 *_editor->tempo_group,
2393 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2395 *new TempoSection (_marker->tempo())
2398 /* use the new marker for the grab */
2399 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2402 TempoMap& map (_editor->session()->tempo_map());
2403 /* get current state */
2404 before_state = &map.get_state();
2405 /* remove the section while we drag it */
2406 map.remove_tempo (section, true);
2410 framepos_t const pf = adjusted_current_frame (event);
2411 _marker->set_position (pf);
2412 show_verbose_cursor_time (pf);
2416 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2418 if (!movement_occurred) {
2419 if (was_double_click()) {
2420 _editor->edit_tempo_marker (*_marker);
2425 if (!_marker->tempo().movable()) {
2429 motion (event, false);
2431 TempoMap& map (_editor->session()->tempo_map());
2432 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2433 Timecode::BBT_Time when;
2435 map.bbt_time (beat_time, when);
2437 if (_copy == true) {
2438 _editor->begin_reversible_command (_("copy tempo mark"));
2439 XMLNode &before = map.get_state();
2440 map.add_tempo (_marker->tempo(), when);
2441 XMLNode &after = map.get_state();
2442 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2443 _editor->commit_reversible_command ();
2446 _editor->begin_reversible_command (_("move tempo mark"));
2447 /* we removed it before, so add it back now */
2448 map.add_tempo (_marker->tempo(), when);
2449 XMLNode &after = map.get_state();
2450 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2451 _editor->commit_reversible_command ();
2454 // delete the dummy marker we used for visual representation while moving.
2455 // a new visual marker will show up automatically.
2460 TempoMarkerDrag::aborted (bool moved)
2462 _marker->set_position (_marker->tempo().frame());
2464 TempoMap& map (_editor->session()->tempo_map());
2465 /* we removed it before, so add it back now */
2466 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2467 // delete the dummy marker we used for visual representation while moving.
2468 // a new visual marker will show up automatically.
2473 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2474 : Drag (e, &c.time_bar_canvas_item())
2478 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2481 /** Do all the things we do when dragging the playhead to make it look as though
2482 * we have located, without actually doing the locate (because that would cause
2483 * the diskstream buffers to be refilled, which is too slow).
2486 CursorDrag::fake_locate (framepos_t t)
2488 _editor->playhead_cursor->set_position (t);
2490 Session* s = _editor->session ();
2491 if (s->timecode_transmission_suspended ()) {
2492 framepos_t const f = _editor->playhead_cursor->current_frame ();
2493 /* This is asynchronous so it will be sent "now"
2495 s->send_mmc_locate (f);
2496 /* These are synchronous and will be sent during the next
2499 s->queue_full_time_code ();
2500 s->queue_song_position_pointer ();
2503 show_verbose_cursor_time (t);
2504 _editor->UpdateAllTransportClocks (t);
2508 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2510 Drag::start_grab (event, c);
2512 _grab_zoom = _editor->samples_per_pixel;
2514 framepos_t where = _editor->canvas_event_sample (event);
2516 _editor->snap_to_with_modifier (where, event);
2518 _editor->_dragging_playhead = true;
2520 Session* s = _editor->session ();
2522 /* grab the track canvas item as well */
2524 _cursor.track_canvas_item().grab();
2527 if (_was_rolling && _stop) {
2531 if (s->is_auditioning()) {
2532 s->cancel_audition ();
2536 if (AudioEngine::instance()->connected()) {
2538 /* do this only if we're the engine is connected
2539 * because otherwise this request will never be
2540 * serviced and we'll busy wait forever. likewise,
2541 * notice if we are disconnected while waiting for the
2542 * request to be serviced.
2545 s->request_suspend_timecode_transmission ();
2546 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2547 /* twiddle our thumbs */
2552 fake_locate (where);
2556 CursorDrag::motion (GdkEvent* event, bool)
2558 framepos_t const adjusted_frame = adjusted_current_frame (event);
2559 if (adjusted_frame != last_pointer_frame()) {
2560 fake_locate (adjusted_frame);
2565 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2567 _editor->_dragging_playhead = false;
2569 _cursor.track_canvas_item().ungrab();
2571 if (!movement_occurred && _stop) {
2575 motion (event, false);
2577 Session* s = _editor->session ();
2579 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2580 _editor->_pending_locate_request = true;
2581 s->request_resume_timecode_transmission ();
2586 CursorDrag::aborted (bool)
2588 _cursor.track_canvas_item().ungrab();
2590 if (_editor->_dragging_playhead) {
2591 _editor->session()->request_resume_timecode_transmission ();
2592 _editor->_dragging_playhead = false;
2595 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2598 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2599 : RegionDrag (e, i, p, v)
2601 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2605 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2607 Drag::start_grab (event, cursor);
2609 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2610 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2612 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2616 FadeInDrag::setup_pointer_frame_offset ()
2618 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2619 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2620 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2624 FadeInDrag::motion (GdkEvent* event, bool)
2626 framecnt_t fade_length;
2628 framepos_t const pos = adjusted_current_frame (event);
2630 boost::shared_ptr<Region> region = _primary->region ();
2632 if (pos < (region->position() + 64)) {
2633 fade_length = 64; // this should be a minimum defined somewhere
2634 } else if (pos > region->last_frame()) {
2635 fade_length = region->length();
2637 fade_length = pos - region->position();
2640 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2642 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2648 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2651 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2655 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2657 if (!movement_occurred) {
2661 framecnt_t fade_length;
2663 framepos_t const pos = adjusted_current_frame (event);
2665 boost::shared_ptr<Region> region = _primary->region ();
2667 if (pos < (region->position() + 64)) {
2668 fade_length = 64; // this should be a minimum defined somewhere
2669 } else if (pos > region->last_frame()) {
2670 fade_length = region->length();
2672 fade_length = pos - region->position();
2675 _editor->begin_reversible_command (_("change fade in length"));
2677 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2679 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2685 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2686 XMLNode &before = alist->get_state();
2688 tmp->audio_region()->set_fade_in_length (fade_length);
2689 tmp->audio_region()->set_fade_in_active (true);
2691 XMLNode &after = alist->get_state();
2692 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2695 _editor->commit_reversible_command ();
2699 FadeInDrag::aborted (bool)
2701 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2702 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2708 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2712 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2713 : RegionDrag (e, i, p, v)
2715 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2719 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2721 Drag::start_grab (event, cursor);
2723 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2724 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2726 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2730 FadeOutDrag::setup_pointer_frame_offset ()
2732 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2733 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2734 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2738 FadeOutDrag::motion (GdkEvent* event, bool)
2740 framecnt_t fade_length;
2742 framepos_t const pos = adjusted_current_frame (event);
2744 boost::shared_ptr<Region> region = _primary->region ();
2746 if (pos > (region->last_frame() - 64)) {
2747 fade_length = 64; // this should really be a minimum fade defined somewhere
2749 else if (pos < region->position()) {
2750 fade_length = region->length();
2753 fade_length = region->last_frame() - pos;
2756 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2758 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2764 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2767 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2771 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2773 if (!movement_occurred) {
2777 framecnt_t fade_length;
2779 framepos_t const pos = adjusted_current_frame (event);
2781 boost::shared_ptr<Region> region = _primary->region ();
2783 if (pos > (region->last_frame() - 64)) {
2784 fade_length = 64; // this should really be a minimum fade defined somewhere
2786 else if (pos < region->position()) {
2787 fade_length = region->length();
2790 fade_length = region->last_frame() - pos;
2793 _editor->begin_reversible_command (_("change fade out length"));
2795 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2797 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2803 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2804 XMLNode &before = alist->get_state();
2806 tmp->audio_region()->set_fade_out_length (fade_length);
2807 tmp->audio_region()->set_fade_out_active (true);
2809 XMLNode &after = alist->get_state();
2810 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2813 _editor->commit_reversible_command ();
2817 FadeOutDrag::aborted (bool)
2819 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2820 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2826 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2830 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2833 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2835 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2838 _points.push_back (ArdourCanvas::Duple (0, 0));
2839 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2842 MarkerDrag::~MarkerDrag ()
2844 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2849 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2851 location = new Location (*l);
2852 markers.push_back (m);
2857 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2859 Drag::start_grab (event, cursor);
2863 Location *location = _editor->find_location_from_marker (_marker, is_start);
2864 _editor->_dragging_edit_point = true;
2866 update_item (location);
2868 // _drag_line->show();
2869 // _line->raise_to_top();
2872 show_verbose_cursor_time (location->start());
2874 show_verbose_cursor_time (location->end());
2877 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2880 case Selection::Toggle:
2881 /* we toggle on the button release */
2883 case Selection::Set:
2884 if (!_editor->selection->selected (_marker)) {
2885 _editor->selection->set (_marker);
2888 case Selection::Extend:
2890 Locations::LocationList ll;
2891 list<Marker*> to_add;
2893 _editor->selection->markers.range (s, e);
2894 s = min (_marker->position(), s);
2895 e = max (_marker->position(), e);
2898 if (e < max_framepos) {
2901 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2902 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2903 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2906 to_add.push_back (lm->start);
2909 to_add.push_back (lm->end);
2913 if (!to_add.empty()) {
2914 _editor->selection->add (to_add);
2918 case Selection::Add:
2919 _editor->selection->add (_marker);
2923 /* Set up copies for us to manipulate during the drag
2926 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2928 Location* l = _editor->find_location_from_marker (*i, is_start);
2935 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2937 /* range: check that the other end of the range isn't
2940 CopiedLocationInfo::iterator x;
2941 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2942 if (*(*x).location == *l) {
2946 if (x == _copied_locations.end()) {
2947 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2949 (*x).markers.push_back (*i);
2950 (*x).move_both = true;
2958 MarkerDrag::setup_pointer_frame_offset ()
2961 Location *location = _editor->find_location_from_marker (_marker, is_start);
2962 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2966 MarkerDrag::motion (GdkEvent* event, bool)
2968 framecnt_t f_delta = 0;
2970 bool move_both = false;
2971 Location *real_location;
2972 Location *copy_location = 0;
2974 framepos_t const newframe = adjusted_current_frame (event);
2975 framepos_t next = newframe;
2977 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2981 CopiedLocationInfo::iterator x;
2983 /* find the marker we're dragging, and compute the delta */
2985 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2987 copy_location = (*x).location;
2989 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2991 /* this marker is represented by this
2992 * CopiedLocationMarkerInfo
2995 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3000 if (real_location->is_mark()) {
3001 f_delta = newframe - copy_location->start();
3005 switch (_marker->type()) {
3006 case Marker::SessionStart:
3007 case Marker::RangeStart:
3008 case Marker::LoopStart:
3009 case Marker::PunchIn:
3010 f_delta = newframe - copy_location->start();
3013 case Marker::SessionEnd:
3014 case Marker::RangeEnd:
3015 case Marker::LoopEnd:
3016 case Marker::PunchOut:
3017 f_delta = newframe - copy_location->end();
3020 /* what kind of marker is this ? */
3029 if (x == _copied_locations.end()) {
3030 /* hmm, impossible - we didn't find the dragged marker */
3034 /* now move them all */
3036 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3038 copy_location = x->location;
3040 /* call this to find out if its the start or end */
3042 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3046 if (real_location->locked()) {
3050 if (copy_location->is_mark()) {
3054 copy_location->set_start (copy_location->start() + f_delta);
3058 framepos_t new_start = copy_location->start() + f_delta;
3059 framepos_t new_end = copy_location->end() + f_delta;
3061 if (is_start) { // start-of-range marker
3063 if (move_both || (*x).move_both) {
3064 copy_location->set_start (new_start);
3065 copy_location->set_end (new_end);
3066 } else if (new_start < copy_location->end()) {
3067 copy_location->set_start (new_start);
3068 } else if (newframe > 0) {
3069 _editor->snap_to (next, 1, true);
3070 copy_location->set_end (next);
3071 copy_location->set_start (newframe);
3074 } else { // end marker
3076 if (move_both || (*x).move_both) {
3077 copy_location->set_end (new_end);
3078 copy_location->set_start (new_start);
3079 } else if (new_end > copy_location->start()) {
3080 copy_location->set_end (new_end);
3081 } else if (newframe > 0) {
3082 _editor->snap_to (next, -1, true);
3083 copy_location->set_start (next);
3084 copy_location->set_end (newframe);
3089 update_item (copy_location);
3091 /* now lookup the actual GUI items used to display this
3092 * location and move them to wherever the copy of the location
3093 * is now. This means that the logic in ARDOUR::Location is
3094 * still enforced, even though we are not (yet) modifying
3095 * the real Location itself.
3098 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3101 lm->set_position (copy_location->start(), copy_location->end());
3106 assert (!_copied_locations.empty());
3108 show_verbose_cursor_time (newframe);
3112 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3114 if (!movement_occurred) {
3116 if (was_double_click()) {
3117 _editor->rename_marker (_marker);
3121 /* just a click, do nothing but finish
3122 off the selection process
3125 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3128 case Selection::Set:
3129 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3130 _editor->selection->set (_marker);
3134 case Selection::Toggle:
3135 /* we toggle on the button release, click only */
3136 _editor->selection->toggle (_marker);
3139 case Selection::Extend:
3140 case Selection::Add:
3147 _editor->_dragging_edit_point = false;
3149 _editor->begin_reversible_command ( _("move marker") );
3150 XMLNode &before = _editor->session()->locations()->get_state();
3152 MarkerSelection::iterator i;
3153 CopiedLocationInfo::iterator x;
3156 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3157 x != _copied_locations.end() && i != _editor->selection->markers.end();
3160 Location * location = _editor->find_location_from_marker (*i, is_start);
3164 if (location->locked()) {
3168 if (location->is_mark()) {
3169 location->set_start (((*x).location)->start());
3171 location->set (((*x).location)->start(), ((*x).location)->end());
3176 XMLNode &after = _editor->session()->locations()->get_state();
3177 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3178 _editor->commit_reversible_command ();
3182 MarkerDrag::aborted (bool)
3188 MarkerDrag::update_item (Location*)
3193 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3195 _cumulative_x_drag (0),
3196 _cumulative_y_drag (0)
3198 if (_zero_gain_fraction < 0.0) {
3199 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3202 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3204 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3210 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3212 Drag::start_grab (event, _editor->cursors()->fader);
3214 // start the grab at the center of the control point so
3215 // the point doesn't 'jump' to the mouse after the first drag
3216 _fixed_grab_x = _point->get_x();
3217 _fixed_grab_y = _point->get_y();
3219 float const fraction = 1 - (_point->get_y() / _point->line().height());
3221 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3223 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3224 event->button.x + 10, event->button.y + 10);
3226 _editor->verbose_cursor()->show ();
3228 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3230 if (!_point->can_slide ()) {
3231 _x_constrained = true;
3236 ControlPointDrag::motion (GdkEvent* event, bool)
3238 double dx = _drags->current_pointer_x() - last_pointer_x();
3239 double dy = _drags->current_pointer_y() - last_pointer_y();
3241 if (event->button.state & Keyboard::SecondaryModifier) {
3246 /* coordinate in pixels relative to the start of the region (for region-based automation)
3247 or track (for track-based automation) */
3248 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3249 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3251 // calculate zero crossing point. back off by .01 to stay on the
3252 // positive side of zero
3253 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3255 // make sure we hit zero when passing through
3256 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3260 if (_x_constrained) {
3263 if (_y_constrained) {
3267 _cumulative_x_drag = cx - _fixed_grab_x;
3268 _cumulative_y_drag = cy - _fixed_grab_y;
3272 cy = min ((double) _point->line().height(), cy);
3274 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3276 if (!_x_constrained) {
3277 _editor->snap_to_with_modifier (cx_frames, event);
3280 cx_frames = min (cx_frames, _point->line().maximum_time());
3282 float const fraction = 1.0 - (cy / _point->line().height());
3284 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3286 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3290 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3292 if (!movement_occurred) {
3296 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3297 _editor->reset_point_selection ();
3301 motion (event, false);
3304 _point->line().end_drag (_pushing, _final_index);
3305 _editor->session()->commit_reversible_command ();
3309 ControlPointDrag::aborted (bool)
3311 _point->line().reset ();
3315 ControlPointDrag::active (Editing::MouseMode m)
3317 if (m == Editing::MouseGain) {
3318 /* always active in mouse gain */
3322 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3323 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3326 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3329 _cumulative_y_drag (0)
3331 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3335 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3337 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3340 _item = &_line->grab_item ();
3342 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3343 origin, and ditto for y.
3346 double cx = event->button.x;
3347 double cy = event->button.y;
3349 _line->parent_group().canvas_to_item (cx, cy);
3351 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3356 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3357 /* no adjacent points */
3361 Drag::start_grab (event, _editor->cursors()->fader);
3363 /* store grab start in parent frame */
3368 double fraction = 1.0 - (cy / _line->height());
3370 _line->start_drag_line (before, after, fraction);
3372 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3373 event->button.x + 10, event->button.y + 10);
3375 _editor->verbose_cursor()->show ();
3379 LineDrag::motion (GdkEvent* event, bool)
3381 double dy = _drags->current_pointer_y() - last_pointer_y();
3383 if (event->button.state & Keyboard::SecondaryModifier) {
3387 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3389 _cumulative_y_drag = cy - _fixed_grab_y;
3392 cy = min ((double) _line->height(), cy);
3394 double const fraction = 1.0 - (cy / _line->height());
3397 /* we are ignoring x position for this drag, so we can just pass in anything */
3398 _line->drag_motion (0, fraction, true, false, ignored);
3400 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3404 LineDrag::finished (GdkEvent* event, bool movement_occured)
3406 if (movement_occured) {
3407 motion (event, false);
3408 _line->end_drag (false, 0);
3410 /* add a new control point on the line */
3412 AutomationTimeAxisView* atv;
3414 _line->end_drag (false, 0);
3416 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3417 framepos_t where = _editor->window_event_sample (event, 0, 0);
3418 atv->add_automation_event (event, where, event->button.y, false);
3422 _editor->session()->commit_reversible_command ();
3426 LineDrag::aborted (bool)
3431 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3434 _cumulative_x_drag (0)
3436 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3440 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3442 Drag::start_grab (event);
3444 _line = reinterpret_cast<Line*> (_item);
3447 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3449 double cx = event->button.x;
3450 double cy = event->button.y;
3452 _item->parent()->canvas_to_item (cx, cy);
3454 /* store grab start in parent frame */
3455 _region_view_grab_x = cx;
3457 _before = *(float*) _item->get_data ("position");
3459 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3461 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3465 FeatureLineDrag::motion (GdkEvent*, bool)
3467 double dx = _drags->current_pointer_x() - last_pointer_x();
3469 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3471 _cumulative_x_drag += dx;
3473 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3482 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3484 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3486 float *pos = new float;
3489 _line->set_data ("position", pos);
3495 FeatureLineDrag::finished (GdkEvent*, bool)
3497 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3498 _arv->update_transient(_before, _before);
3502 FeatureLineDrag::aborted (bool)
3507 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3509 , _vertical_only (false)
3511 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3515 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3517 Drag::start_grab (event);
3518 show_verbose_cursor_time (adjusted_current_frame (event));
3522 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3529 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3531 framepos_t grab = grab_frame ();
3532 if (Config->get_rubberbanding_snaps_to_grid ()) {
3533 _editor->snap_to_with_modifier (grab, event);
3536 /* base start and end on initial click position */
3546 if (_drags->current_pointer_y() < grab_y()) {
3547 y1 = _drags->current_pointer_y();
3550 y2 = _drags->current_pointer_y();
3555 if (start != end || y1 != y2) {
3557 double x1 = _editor->sample_to_pixel (start);
3558 double x2 = _editor->sample_to_pixel (end);
3559 const double min_dimension = 2.0;
3561 _editor->rubberband_rect->set_x0 (x1);
3562 if (_vertical_only) {
3563 /* fixed 10 pixel width */
3564 _editor->rubberband_rect->set_x1 (x1 + 10);
3567 x2 = min (x1 - min_dimension, x2);
3569 x2 = max (x1 + min_dimension, x2);
3571 _editor->rubberband_rect->set_x1 (x2);
3574 _editor->rubberband_rect->set_y0 (y1);
3576 y2 = min (y1 - min_dimension, y2);
3578 y2 = max (y1 + min_dimension, y2);
3581 _editor->rubberband_rect->set_y1 (y2);
3583 _editor->rubberband_rect->show();
3584 _editor->rubberband_rect->raise_to_top();
3586 show_verbose_cursor_time (pf);
3588 do_select_things (event, true);
3593 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3598 if (grab_frame() < last_pointer_frame()) {
3600 x2 = last_pointer_frame ();
3603 x1 = last_pointer_frame ();
3609 if (_drags->current_pointer_y() < grab_y()) {
3610 y1 = _drags->current_pointer_y();
3613 y2 = _drags->current_pointer_y();
3617 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3621 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3623 if (movement_occurred) {
3625 motion (event, false);
3626 do_select_things (event, false);
3632 bool do_deselect = true;
3633 MidiTimeAxisView* mtv;
3635 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3637 if (_editor->selection->empty()) {
3638 /* nothing selected */
3639 add_midi_region (mtv);
3640 do_deselect = false;
3644 /* do not deselect if Primary or Tertiary (toggle-select or
3645 * extend-select are pressed.
3648 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3649 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3656 _editor->rubberband_rect->hide();
3660 RubberbandSelectDrag::aborted (bool)
3662 _editor->rubberband_rect->hide ();
3665 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3666 : RegionDrag (e, i, p, v)
3668 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3672 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3674 Drag::start_grab (event, cursor);
3676 show_verbose_cursor_time (adjusted_current_frame (event));
3680 TimeFXDrag::motion (GdkEvent* event, bool)
3682 RegionView* rv = _primary;
3683 StreamView* cv = rv->get_time_axis_view().view ();
3685 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3686 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3687 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3689 framepos_t const pf = adjusted_current_frame (event);
3691 if (pf > rv->region()->position()) {
3692 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3695 show_verbose_cursor_time (pf);
3699 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3701 _primary->get_time_axis_view().hide_timestretch ();
3703 if (!movement_occurred) {
3707 if (last_pointer_frame() < _primary->region()->position()) {
3708 /* backwards drag of the left edge - not usable */
3712 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3714 float percentage = (double) newlen / (double) _primary->region()->length();
3716 #ifndef USE_RUBBERBAND
3717 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3718 if (_primary->region()->data_type() == DataType::AUDIO) {
3719 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3723 if (!_editor->get_selection().regions.empty()) {
3724 /* primary will already be included in the selection, and edit
3725 group shared editing will propagate selection across
3726 equivalent regions, so just use the current region
3730 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3731 error << _("An error occurred while executing time stretch operation") << endmsg;
3737 TimeFXDrag::aborted (bool)
3739 _primary->get_time_axis_view().hide_timestretch ();
3742 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3745 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3749 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3751 Drag::start_grab (event);
3755 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3757 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3761 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3763 if (movement_occurred && _editor->session()) {
3764 /* make sure we stop */
3765 _editor->session()->request_transport_speed (0.0);
3770 ScrubDrag::aborted (bool)
3775 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3780 , _original_pointer_time_axis (-1)
3781 , _last_pointer_time_axis (-1)
3782 , _time_selection_at_start (!_editor->get_selection().time.empty())
3784 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3786 if (_time_selection_at_start) {
3787 start_at_start = _editor->get_selection().time.start();
3788 end_at_start = _editor->get_selection().time.end_frame();
3793 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3795 if (_editor->session() == 0) {
3799 Gdk::Cursor* cursor = 0;
3801 switch (_operation) {
3802 case CreateSelection:
3803 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3808 cursor = _editor->cursors()->selector;
3809 Drag::start_grab (event, cursor);
3812 case SelectionStartTrim:
3813 if (_editor->clicked_axisview) {
3814 _editor->clicked_axisview->order_selection_trims (_item, true);
3816 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3819 case SelectionEndTrim:
3820 if (_editor->clicked_axisview) {
3821 _editor->clicked_axisview->order_selection_trims (_item, false);
3823 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3827 Drag::start_grab (event, cursor);
3830 case SelectionExtend:
3831 Drag::start_grab (event, cursor);
3835 if (_operation == SelectionMove) {
3836 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3838 show_verbose_cursor_time (adjusted_current_frame (event));
3841 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3845 SelectionDrag::setup_pointer_frame_offset ()
3847 switch (_operation) {
3848 case CreateSelection:
3849 _pointer_frame_offset = 0;
3852 case SelectionStartTrim:
3854 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3857 case SelectionEndTrim:
3858 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3861 case SelectionExtend:
3867 SelectionDrag::motion (GdkEvent* event, bool first_move)
3869 framepos_t start = 0;
3871 framecnt_t length = 0;
3872 framecnt_t distance = 0;
3874 framepos_t const pending_position = adjusted_current_frame (event);
3876 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3880 switch (_operation) {
3881 case CreateSelection:
3883 framepos_t grab = grab_frame ();
3886 grab = adjusted_current_frame (event, false);
3887 if (grab < pending_position) {
3888 _editor->snap_to (grab, -1);
3890 _editor->snap_to (grab, 1);
3894 if (pending_position < grab) {
3895 start = pending_position;
3898 end = pending_position;
3902 /* first drag: Either add to the selection
3903 or create a new selection
3910 /* adding to the selection */
3911 _editor->set_selected_track_as_side_effect (Selection::Add);
3912 _editor->clicked_selection = _editor->selection->add (start, end);
3919 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3920 _editor->set_selected_track_as_side_effect (Selection::Set);
3923 _editor->clicked_selection = _editor->selection->set (start, end);
3927 /* select all tracks within the rectangle that we've marked out so far */
3928 TrackViewList to_be_added_to_selection;
3929 TrackViewList to_be_removed_from_selection;
3930 TrackViewList& all_tracks (_editor->track_views);
3932 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3934 if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
3935 if (!(*i)->get_selected()) {
3936 to_be_added_to_selection.push_back (*i);
3939 if ((*i)->get_selected()) {
3940 to_be_removed_from_selection.push_back (*i);
3945 if (!to_be_added_to_selection.empty()) {
3946 _editor->selection->add (to_be_added_to_selection);
3949 if (!to_be_removed_from_selection.empty()) {
3950 _editor->selection->remove (to_be_removed_from_selection);
3955 case SelectionStartTrim:
3957 start = _editor->selection->time[_editor->clicked_selection].start;
3958 end = _editor->selection->time[_editor->clicked_selection].end;
3960 if (pending_position > end) {
3963 start = pending_position;
3967 case SelectionEndTrim:
3969 start = _editor->selection->time[_editor->clicked_selection].start;
3970 end = _editor->selection->time[_editor->clicked_selection].end;
3972 if (pending_position < start) {
3975 end = pending_position;
3982 start = _editor->selection->time[_editor->clicked_selection].start;
3983 end = _editor->selection->time[_editor->clicked_selection].end;
3985 length = end - start;
3986 distance = pending_position - start;
3987 start = pending_position;
3988 _editor->snap_to (start);
3990 end = start + length;
3994 case SelectionExtend:
3998 _editor->maybe_autoscroll (true, false, false);
4001 switch (_operation) {
4003 if (_time_selection_at_start) {
4004 _editor->selection->move_time (distance);
4008 _editor->selection->replace (_editor->clicked_selection, start, end);
4012 if (_operation == SelectionMove) {
4013 show_verbose_cursor_time(start);
4015 show_verbose_cursor_time(pending_position);
4020 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4022 Session* s = _editor->session();
4024 if (movement_occurred) {
4025 motion (event, false);
4026 /* XXX this is not object-oriented programming at all. ick */
4027 if (_editor->selection->time.consolidate()) {
4028 _editor->selection->TimeChanged ();
4031 /* XXX what if its a music time selection? */
4033 if ( s->get_play_range() && s->transport_rolling() ) {
4034 s->request_play_range (&_editor->selection->time, true);
4036 if (Config->get_always_play_range() && !s->transport_rolling()) {
4037 s->request_locate (_editor->get_selection().time.start());
4043 /* just a click, no pointer movement.
4046 if (_operation == SelectionExtend) {
4047 if (_time_selection_at_start) {
4048 framepos_t pos = adjusted_current_frame (event, false);
4049 framepos_t start = min (pos, start_at_start);
4050 framepos_t end = max (pos, end_at_start);
4051 _editor->selection->set (start, end);
4054 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4055 if (_editor->clicked_selection) {
4056 _editor->selection->remove (_editor->clicked_selection);
4059 if (!_editor->clicked_selection) {
4060 _editor->selection->clear_time();
4065 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4066 _editor->selection->set (_editor->clicked_axisview);
4069 if (s && s->get_play_range () && s->transport_rolling()) {
4070 s->request_stop (false, false);
4075 _editor->stop_canvas_autoscroll ();
4076 _editor->clicked_selection = 0;
4080 SelectionDrag::aborted (bool)
4085 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4090 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4092 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4093 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4094 physical_screen_height (_editor->get_window())));
4095 _drag_rect->hide ();
4097 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4098 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4102 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4104 if (_editor->session() == 0) {
4108 Gdk::Cursor* cursor = 0;
4110 if (!_editor->temp_location) {
4111 _editor->temp_location = new Location (*_editor->session());
4114 switch (_operation) {
4115 case CreateRangeMarker:
4116 case CreateTransportMarker:
4117 case CreateCDMarker:
4119 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4124 cursor = _editor->cursors()->selector;
4128 Drag::start_grab (event, cursor);
4130 show_verbose_cursor_time (adjusted_current_frame (event));
4134 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4136 framepos_t start = 0;
4138 ArdourCanvas::Rectangle *crect;
4140 switch (_operation) {
4141 case CreateRangeMarker:
4142 crect = _editor->range_bar_drag_rect;
4144 case CreateTransportMarker:
4145 crect = _editor->transport_bar_drag_rect;
4147 case CreateCDMarker:
4148 crect = _editor->cd_marker_bar_drag_rect;
4151 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4156 framepos_t const pf = adjusted_current_frame (event);
4158 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4159 framepos_t grab = grab_frame ();
4160 _editor->snap_to (grab);
4162 if (pf < grab_frame()) {
4170 /* first drag: Either add to the selection
4171 or create a new selection.
4176 _editor->temp_location->set (start, end);
4180 update_item (_editor->temp_location);
4182 //_drag_rect->raise_to_top();
4187 _editor->maybe_autoscroll (true, false, false);
4190 _editor->temp_location->set (start, end);
4192 double x1 = _editor->sample_to_pixel (start);
4193 double x2 = _editor->sample_to_pixel (end);
4197 update_item (_editor->temp_location);
4200 show_verbose_cursor_time (pf);
4205 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4207 Location * newloc = 0;
4211 if (movement_occurred) {
4212 motion (event, false);
4215 switch (_operation) {
4216 case CreateRangeMarker:
4217 case CreateCDMarker:
4219 _editor->begin_reversible_command (_("new range marker"));
4220 XMLNode &before = _editor->session()->locations()->get_state();
4221 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4222 if (_operation == CreateCDMarker) {
4223 flags = Location::IsRangeMarker | Location::IsCDMarker;
4224 _editor->cd_marker_bar_drag_rect->hide();
4227 flags = Location::IsRangeMarker;
4228 _editor->range_bar_drag_rect->hide();
4230 newloc = new Location (
4231 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4234 _editor->session()->locations()->add (newloc, true);
4235 XMLNode &after = _editor->session()->locations()->get_state();
4236 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4237 _editor->commit_reversible_command ();
4241 case CreateTransportMarker:
4242 // popup menu to pick loop or punch
4243 _editor->new_transport_marker_context_menu (&event->button, _item);
4249 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4251 if (_operation == CreateTransportMarker) {
4253 /* didn't drag, so just locate */
4255 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4257 } else if (_operation == CreateCDMarker) {
4259 /* didn't drag, but mark is already created so do
4262 } else { /* operation == CreateRangeMarker */
4268 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4270 if (end == max_framepos) {
4271 end = _editor->session()->current_end_frame ();
4274 if (start == max_framepos) {
4275 start = _editor->session()->current_start_frame ();
4278 switch (_editor->mouse_mode) {
4280 /* find the two markers on either side and then make the selection from it */
4281 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4285 /* find the two markers on either side of the click and make the range out of it */
4286 _editor->selection->set (start, end);
4295 _editor->stop_canvas_autoscroll ();
4299 RangeMarkerBarDrag::aborted (bool)
4305 RangeMarkerBarDrag::update_item (Location* location)
4307 double const x1 = _editor->sample_to_pixel (location->start());
4308 double const x2 = _editor->sample_to_pixel (location->end());
4310 _drag_rect->set_x0 (x1);
4311 _drag_rect->set_x1 (x2);
4314 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4318 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4322 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4324 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4325 Drag::start_grab (event, _editor->cursors()->zoom_out);
4328 Drag::start_grab (event, _editor->cursors()->zoom_in);
4332 show_verbose_cursor_time (adjusted_current_frame (event));
4336 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4341 framepos_t const pf = adjusted_current_frame (event);
4343 framepos_t grab = grab_frame ();
4344 _editor->snap_to_with_modifier (grab, event);
4346 /* base start and end on initial click position */
4358 _editor->zoom_rect->show();
4359 _editor->zoom_rect->raise_to_top();
4362 _editor->reposition_zoom_rect(start, end);
4364 show_verbose_cursor_time (pf);
4369 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4371 if (movement_occurred) {
4372 motion (event, false);
4374 if (grab_frame() < last_pointer_frame()) {
4375 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4377 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4380 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4381 _editor->tav_zoom_step (_zoom_out);
4383 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4387 _editor->zoom_rect->hide();
4391 MouseZoomDrag::aborted (bool)
4393 _editor->zoom_rect->hide ();
4396 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4398 , _cumulative_dx (0)
4399 , _cumulative_dy (0)
4401 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4403 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4405 _region = &_primary->region_view ();
4406 _note_height = _region->midi_stream_view()->note_height ();
4410 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4412 Drag::start_grab (event);
4414 if (!(_was_selected = _primary->selected())) {
4416 /* tertiary-click means extend selection - we'll do that on button release,
4417 so don't add it here, because otherwise we make it hard to figure
4418 out the "extend-to" range.
4421 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4424 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4427 _region->note_selected (_primary, true);
4429 _region->unique_select (_primary);
4435 /** @return Current total drag x change in frames */
4437 NoteDrag::total_dx () const
4440 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4442 /* primary note time */
4443 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4445 /* new time of the primary note in session frames */
4446 frameoffset_t st = n + dx;
4448 framepos_t const rp = _region->region()->position ();
4450 /* prevent the note being dragged earlier than the region's position */
4453 /* snap and return corresponding delta */
4454 return _region->snap_frame_to_frame (st - rp) + rp - n;
4457 /** @return Current total drag y change in note number */
4459 NoteDrag::total_dy () const
4461 MidiStreamView* msv = _region->midi_stream_view ();
4462 double const y = _region->midi_view()->y_position ();
4463 /* new current note */
4464 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4466 n = max (msv->lowest_note(), n);
4467 n = min (msv->highest_note(), n);
4468 /* and work out delta */
4469 return n - msv->y_to_note (grab_y() - y);
4473 NoteDrag::motion (GdkEvent *, bool)
4475 /* Total change in x and y since the start of the drag */
4476 frameoffset_t const dx = total_dx ();
4477 int8_t const dy = total_dy ();
4479 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4480 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4481 double const tdy = -dy * _note_height - _cumulative_dy;
4484 _cumulative_dx += tdx;
4485 _cumulative_dy += tdy;
4487 int8_t note_delta = total_dy();
4489 _region->move_selection (tdx, tdy, note_delta);
4491 /* the new note value may be the same as the old one, but we
4492 * don't know what that means because the selection may have
4493 * involved more than one note and we might be doing something
4494 * odd with them. so show the note value anyway, always.
4498 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4500 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4501 (int) floor ((double)new_note));
4503 show_verbose_cursor_text (buf);
4508 NoteDrag::finished (GdkEvent* ev, bool moved)
4511 /* no motion - select note */
4513 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4514 _editor->current_mouse_mode() == Editing::MouseDraw) {
4516 if (_was_selected) {
4517 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4519 _region->note_deselected (_primary);
4522 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4523 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4525 if (!extend && !add && _region->selection_size() > 1) {
4526 _region->unique_select (_primary);
4527 } else if (extend) {
4528 _region->note_selected (_primary, true, true);
4530 /* it was added during button press */
4535 _region->note_dropped (_primary, total_dx(), total_dy());
4540 NoteDrag::aborted (bool)
4545 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4546 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4547 : Drag (editor, atv->base_item ())
4549 , _nothing_to_drag (false)
4551 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4552 y_origin = atv->y_position();
4553 setup (atv->lines ());
4556 /** Make an AutomationRangeDrag for region gain lines */
4557 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4558 : Drag (editor, rv->get_canvas_group ())
4560 , _nothing_to_drag (false)
4562 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4564 list<boost::shared_ptr<AutomationLine> > lines;
4565 lines.push_back (rv->get_gain_line ());
4566 y_origin = rv->get_time_axis_view().y_position();
4570 /** @param lines AutomationLines to drag.
4571 * @param offset Offset from the session start to the points in the AutomationLines.
4574 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4576 /* find the lines that overlap the ranges being dragged */
4577 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4578 while (i != lines.end ()) {
4579 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4582 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4584 /* check this range against all the AudioRanges that we are using */
4585 list<AudioRange>::const_iterator k = _ranges.begin ();
4586 while (k != _ranges.end()) {
4587 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4593 /* add it to our list if it overlaps at all */
4594 if (k != _ranges.end()) {
4599 _lines.push_back (n);
4605 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4609 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4611 return 1.0 - ((global_y - y_origin) / line->height());
4615 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4617 Drag::start_grab (event, cursor);
4619 /* Get line states before we start changing things */
4620 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4621 i->state = &i->line->get_state ();
4622 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4625 if (_ranges.empty()) {
4627 /* No selected time ranges: drag all points */
4628 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4629 uint32_t const N = i->line->npoints ();
4630 for (uint32_t j = 0; j < N; ++j) {
4631 i->points.push_back (i->line->nth (j));
4637 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4639 framecnt_t const half = (i->start + i->end) / 2;
4641 /* find the line that this audio range starts in */
4642 list<Line>::iterator j = _lines.begin();
4643 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4647 if (j != _lines.end()) {
4648 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4650 /* j is the line that this audio range starts in; fade into it;
4651 64 samples length plucked out of thin air.
4654 framepos_t a = i->start + 64;
4659 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4660 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4662 the_list->add (p, the_list->eval (p));
4663 the_list->add (q, the_list->eval (q));
4666 /* same thing for the end */
4669 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4673 if (j != _lines.end()) {
4674 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4676 /* j is the line that this audio range starts in; fade out of it;
4677 64 samples length plucked out of thin air.
4680 framepos_t b = i->end - 64;
4685 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4686 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4688 the_list->add (p, the_list->eval (p));
4689 the_list->add (q, the_list->eval (q));
4693 _nothing_to_drag = true;
4695 /* Find all the points that should be dragged and put them in the relevant
4696 points lists in the Line structs.
4699 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4701 uint32_t const N = i->line->npoints ();
4702 for (uint32_t j = 0; j < N; ++j) {
4704 /* here's a control point on this line */
4705 ControlPoint* p = i->line->nth (j);
4706 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4708 /* see if it's inside a range */
4709 list<AudioRange>::const_iterator k = _ranges.begin ();
4710 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4714 if (k != _ranges.end()) {
4715 /* dragging this point */
4716 _nothing_to_drag = false;
4717 i->points.push_back (p);
4723 if (_nothing_to_drag) {
4727 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4728 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4733 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4735 if (_nothing_to_drag) {
4739 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4740 float const f = y_fraction (l->line, _drags->current_pointer_y());
4741 /* we are ignoring x position for this drag, so we can just pass in anything */
4743 l->line->drag_motion (0, f, true, false, ignored);
4744 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4749 AutomationRangeDrag::finished (GdkEvent* event, bool)
4751 if (_nothing_to_drag) {
4755 motion (event, false);
4756 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4757 i->line->end_drag (false, 0);
4760 _editor->session()->commit_reversible_command ();
4764 AutomationRangeDrag::aborted (bool)
4766 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4771 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4774 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4775 layer = v->region()->layer ();
4776 initial_y = v->get_canvas_group()->position().y;
4777 initial_playlist = v->region()->playlist ();
4778 initial_position = v->region()->position ();
4779 initial_end = v->region()->position () + v->region()->length ();
4782 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4783 : Drag (e, i->canvas_item ())
4786 , _cumulative_dx (0)
4788 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4789 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4794 PatchChangeDrag::motion (GdkEvent* ev, bool)
4796 framepos_t f = adjusted_current_frame (ev);
4797 boost::shared_ptr<Region> r = _region_view->region ();
4798 f = max (f, r->position ());
4799 f = min (f, r->last_frame ());
4801 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4802 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4803 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4804 _cumulative_dx = dxu;
4808 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4810 if (!movement_occurred) {
4814 boost::shared_ptr<Region> r (_region_view->region ());
4815 framepos_t f = adjusted_current_frame (ev);
4816 f = max (f, r->position ());
4817 f = min (f, r->last_frame ());
4819 _region_view->move_patch_change (
4821 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4826 PatchChangeDrag::aborted (bool)
4828 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4832 PatchChangeDrag::setup_pointer_frame_offset ()
4834 boost::shared_ptr<Region> region = _region_view->region ();
4835 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4838 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4839 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4846 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4848 framepos_t const p = _region_view->region()->position ();
4849 double const y = _region_view->midi_view()->y_position ();
4851 x1 = max ((framepos_t) 0, x1 - p);
4852 x2 = max ((framepos_t) 0, x2 - p);
4853 y1 = max (0.0, y1 - y);
4854 y2 = max (0.0, y2 - y);
4856 _region_view->update_drag_selection (
4857 _editor->sample_to_pixel (x1),
4858 _editor->sample_to_pixel (x2),
4861 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4866 MidiRubberbandSelectDrag::deselect_things ()
4871 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4872 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4875 _vertical_only = true;
4879 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4881 double const y = _region_view->midi_view()->y_position ();
4883 y1 = max (0.0, y1 - y);
4884 y2 = max (0.0, y2 - y);
4886 _region_view->update_vertical_drag_selection (
4889 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4894 MidiVerticalSelectDrag::deselect_things ()
4899 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4900 : RubberbandSelectDrag (e, i)
4906 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4908 if (drag_in_progress) {
4909 /* We just want to select things at the end of the drag, not during it */
4913 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4915 _editor->begin_reversible_command (_("rubberband selection"));
4916 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4917 _editor->commit_reversible_command ();
4921 EditorRubberbandSelectDrag::deselect_things ()
4923 if (!getenv("ARDOUR_SAE")) {
4924 _editor->selection->clear_tracks();
4926 _editor->selection->clear_regions();
4927 _editor->selection->clear_points ();
4928 _editor->selection->clear_lines ();
4931 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4939 NoteCreateDrag::~NoteCreateDrag ()
4945 NoteCreateDrag::grid_frames (framepos_t t) const
4948 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4953 return _region_view->region_beats_to_region_frames (grid_beats);
4957 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4959 Drag::start_grab (event, cursor);
4961 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4963 framepos_t pf = _drags->current_pointer_frame ();
4964 framecnt_t const g = grid_frames (pf);
4966 /* Hack so that we always snap to the note that we are over, instead of snapping
4967 to the next one if we're more than halfway through the one we're over.
4969 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4973 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4975 MidiStreamView* sv = _region_view->midi_stream_view ();
4976 double const x = _editor->sample_to_pixel (_note[0]);
4977 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4979 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4980 _drag_rect->set_outline_all ();
4981 _drag_rect->set_outline_color (0xffffff99);
4982 _drag_rect->set_fill_color (0xffffff66);
4986 NoteCreateDrag::motion (GdkEvent* event, bool)
4988 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4989 double const x = _editor->sample_to_pixel (_note[1]);
4990 if (_note[1] > _note[0]) {
4991 _drag_rect->set_x1 (x);
4993 _drag_rect->set_x0 (x);
4998 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5000 if (!had_movement) {
5004 framepos_t const start = min (_note[0], _note[1]);
5005 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5007 framecnt_t const g = grid_frames (start);
5008 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5010 if (_editor->snap_mode() == SnapNormal && length < g) {
5011 length = g - one_tick;
5014 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5016 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5020 NoteCreateDrag::y_to_region (double y) const
5023 _region_view->get_canvas_group()->canvas_to_item (x, y);
5028 NoteCreateDrag::aborted (bool)
5033 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5038 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5042 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5044 Drag::start_grab (event, cursor);
5048 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5054 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5057 distance = _drags->current_pointer_x() - grab_x();
5058 len = ar->fade_in()->back()->when;
5060 distance = grab_x() - _drags->current_pointer_x();
5061 len = ar->fade_out()->back()->when;
5064 /* how long should it be ? */
5066 new_length = len + _editor->pixel_to_sample (distance);
5068 /* now check with the region that this is legal */
5070 new_length = ar->verify_xfade_bounds (new_length, start);
5073 arv->reset_fade_in_shape_width (ar, new_length);
5075 arv->reset_fade_out_shape_width (ar, new_length);
5080 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5086 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5089 distance = _drags->current_pointer_x() - grab_x();
5090 len = ar->fade_in()->back()->when;
5092 distance = grab_x() - _drags->current_pointer_x();
5093 len = ar->fade_out()->back()->when;
5096 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5098 _editor->begin_reversible_command ("xfade trim");
5099 ar->playlist()->clear_owned_changes ();
5102 ar->set_fade_in_length (new_length);
5104 ar->set_fade_out_length (new_length);
5107 /* Adjusting the xfade may affect other regions in the playlist, so we need
5108 to get undo Commands from the whole playlist rather than just the
5112 vector<Command*> cmds;
5113 ar->playlist()->rdiff (cmds);
5114 _editor->session()->add_commands (cmds);
5115 _editor->commit_reversible_command ();
5120 CrossfadeEdgeDrag::aborted (bool)
5123 arv->redraw_start_xfade ();
5125 arv->redraw_end_xfade ();