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 const 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";
1946 _editor->begin_reversible_command (trim_type);
1948 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1949 RegionView* rv = i->view;
1950 rv->fake_set_opaque (false);
1951 rv->enable_display (false);
1952 rv->region()->playlist()->clear_owned_changes ();
1954 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1957 arv->temporarily_hide_envelope ();
1961 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1962 insert_result = _editor->motion_frozen_playlists.insert (pl);
1964 if (insert_result.second) {
1970 bool non_overlap_trim = false;
1972 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1973 non_overlap_trim = true;
1976 switch (_operation) {
1978 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1979 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1980 if (changed && _preserve_fade_anchor) {
1981 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1986 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1987 distance = _drags->current_pointer_x() - grab_x();
1988 len = ar->fade_in()->back()->when;
1989 new_length = len - _editor->pixel_to_sample (distance);
1990 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1991 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1998 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1999 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2000 if (changed && _preserve_fade_anchor) {
2001 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2006 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2007 distance = grab_x() - _drags->current_pointer_x();
2008 len = ar->fade_out()->back()->when;
2009 new_length = len - _editor->pixel_to_sample (distance);
2010 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2011 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
2019 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2021 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2022 i->view->move_contents (frame_delta);
2028 switch (_operation) {
2030 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2033 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2036 // show_verbose_cursor_time (frame_delta);
2043 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2045 if (movement_occurred) {
2046 motion (event, false);
2048 if (_operation == StartTrim) {
2049 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2051 /* This must happen before the region's StatefulDiffCommand is created, as it may
2052 `correct' (ahem) the region's _start from being negative to being zero. It
2053 needs to be zero in the undo record.
2055 i->view->trim_front_ending ();
2057 if (_preserve_fade_anchor) {
2058 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2063 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2064 distance = _drags->current_pointer_x() - grab_x();
2065 len = ar->fade_in()->back()->when;
2066 new_length = len - _editor->pixel_to_sample (distance);
2067 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2068 ar->set_fade_in_length(new_length);
2071 if (_jump_position_when_done) {
2072 i->view->region()->set_position (i->initial_position);
2075 } else if (_operation == EndTrim) {
2076 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2077 if (_preserve_fade_anchor) {
2078 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2083 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2084 distance = _drags->current_pointer_x() - grab_x();
2085 len = ar->fade_out()->back()->when;
2086 new_length = len - _editor->pixel_to_sample (distance);
2087 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2088 ar->set_fade_out_length(new_length);
2091 if (_jump_position_when_done) {
2092 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2097 if (!_views.empty()) {
2098 if (_operation == StartTrim) {
2099 _editor->maybe_locate_with_edit_preroll(
2100 _views.begin()->view->region()->position());
2102 if (_operation == EndTrim) {
2103 _editor->maybe_locate_with_edit_preroll(
2104 _views.begin()->view->region()->position() +
2105 _views.begin()->view->region()->length());
2109 if (!_editor->selection->selected (_primary)) {
2110 _primary->thaw_after_trim ();
2113 set<boost::shared_ptr<Playlist> > diffed_playlists;
2115 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2116 i->view->thaw_after_trim ();
2117 i->view->enable_display (true);
2118 i->view->fake_set_opaque (true);
2120 /* Trimming one region may affect others on the playlist, so we need
2121 to get undo Commands from the whole playlist rather than just the
2122 region. Use diffed_playlists to make sure we don't diff a given
2123 playlist more than once.
2125 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2126 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2127 vector<Command*> cmds;
2129 _editor->session()->add_commands (cmds);
2130 diffed_playlists.insert (p);
2135 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2139 _editor->motion_frozen_playlists.clear ();
2140 _editor->commit_reversible_command();
2143 /* no mouse movement */
2144 _editor->point_trim (event, adjusted_current_frame (event));
2147 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2148 if (_operation == StartTrim) {
2149 i->view->trim_front_ending ();
2152 i->view->region()->resume_property_changes ();
2157 TrimDrag::aborted (bool movement_occurred)
2159 /* Our motion method is changing model state, so use the Undo system
2160 to cancel. Perhaps not ideal, as this will leave an Undo point
2161 behind which may be slightly odd from the user's point of view.
2166 if (movement_occurred) {
2170 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2171 i->view->region()->resume_property_changes ();
2176 TrimDrag::setup_pointer_frame_offset ()
2178 list<DraggingView>::iterator i = _views.begin ();
2179 while (i != _views.end() && i->view != _primary) {
2183 if (i == _views.end()) {
2187 switch (_operation) {
2189 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2192 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2199 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2203 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2204 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2209 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2211 Drag::start_grab (event, cursor);
2212 show_verbose_cursor_time (adjusted_current_frame(event));
2216 MeterMarkerDrag::setup_pointer_frame_offset ()
2218 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2222 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2224 if (!_marker->meter().movable()) {
2230 // create a dummy marker for visual representation of moving the
2231 // section, because whether its a copy or not, we're going to
2232 // leave or lose the original marker (leave if its a copy; lose if its
2233 // not, because we'll remove it from the map).
2235 MeterSection section (_marker->meter());
2237 if (!section.movable()) {
2242 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2244 _marker = new MeterMarker (
2246 *_editor->meter_group,
2247 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2249 *new MeterSection (_marker->meter())
2252 /* use the new marker for the grab */
2253 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2256 TempoMap& map (_editor->session()->tempo_map());
2257 /* get current state */
2258 before_state = &map.get_state();
2259 /* remove the section while we drag it */
2260 map.remove_meter (section, true);
2264 framepos_t const pf = adjusted_current_frame (event);
2265 _marker->set_position (pf);
2266 show_verbose_cursor_time (pf);
2270 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2272 if (!movement_occurred) {
2273 if (was_double_click()) {
2274 _editor->edit_meter_marker (*_marker);
2279 if (!_marker->meter().movable()) {
2283 motion (event, false);
2285 Timecode::BBT_Time when;
2287 TempoMap& map (_editor->session()->tempo_map());
2288 map.bbt_time (last_pointer_frame(), when);
2290 if (_copy == true) {
2291 _editor->begin_reversible_command (_("copy meter mark"));
2292 XMLNode &before = map.get_state();
2293 map.add_meter (_marker->meter(), when);
2294 XMLNode &after = map.get_state();
2295 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2296 _editor->commit_reversible_command ();
2299 _editor->begin_reversible_command (_("move meter mark"));
2301 /* we removed it before, so add it back now */
2303 map.add_meter (_marker->meter(), when);
2304 XMLNode &after = map.get_state();
2305 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2306 _editor->commit_reversible_command ();
2309 // delete the dummy marker we used for visual representation while moving.
2310 // a new visual marker will show up automatically.
2315 MeterMarkerDrag::aborted (bool moved)
2317 _marker->set_position (_marker->meter().frame ());
2320 TempoMap& map (_editor->session()->tempo_map());
2321 /* we removed it before, so add it back now */
2322 map.add_meter (_marker->meter(), _marker->meter().frame());
2323 // delete the dummy marker we used for visual representation while moving.
2324 // a new visual marker will show up automatically.
2329 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2333 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2335 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2340 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2342 Drag::start_grab (event, cursor);
2343 show_verbose_cursor_time (adjusted_current_frame (event));
2347 TempoMarkerDrag::setup_pointer_frame_offset ()
2349 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2353 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2355 if (!_marker->tempo().movable()) {
2361 // create a dummy marker for visual representation of moving the
2362 // section, because whether its a copy or not, we're going to
2363 // leave or lose the original marker (leave if its a copy; lose if its
2364 // not, because we'll remove it from the map).
2366 // create a dummy marker for visual representation of moving the copy.
2367 // The actual copying is not done before we reach the finish callback.
2370 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2372 TempoSection section (_marker->tempo());
2374 _marker = new TempoMarker (
2376 *_editor->tempo_group,
2377 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2379 *new TempoSection (_marker->tempo())
2382 /* use the new marker for the grab */
2383 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2386 TempoMap& map (_editor->session()->tempo_map());
2387 /* get current state */
2388 before_state = &map.get_state();
2389 /* remove the section while we drag it */
2390 map.remove_tempo (section, true);
2394 framepos_t const pf = adjusted_current_frame (event);
2395 _marker->set_position (pf);
2396 show_verbose_cursor_time (pf);
2400 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2402 if (!movement_occurred) {
2403 if (was_double_click()) {
2404 _editor->edit_tempo_marker (*_marker);
2409 if (!_marker->tempo().movable()) {
2413 motion (event, false);
2415 TempoMap& map (_editor->session()->tempo_map());
2416 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2417 Timecode::BBT_Time when;
2419 map.bbt_time (beat_time, when);
2421 if (_copy == true) {
2422 _editor->begin_reversible_command (_("copy tempo mark"));
2423 XMLNode &before = map.get_state();
2424 map.add_tempo (_marker->tempo(), when);
2425 XMLNode &after = map.get_state();
2426 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2427 _editor->commit_reversible_command ();
2430 _editor->begin_reversible_command (_("move tempo mark"));
2431 /* we removed it before, so add it back now */
2432 map.add_tempo (_marker->tempo(), when);
2433 XMLNode &after = map.get_state();
2434 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2435 _editor->commit_reversible_command ();
2438 // delete the dummy marker we used for visual representation while moving.
2439 // a new visual marker will show up automatically.
2444 TempoMarkerDrag::aborted (bool moved)
2446 _marker->set_position (_marker->tempo().frame());
2448 TempoMap& map (_editor->session()->tempo_map());
2449 /* we removed it before, so add it back now */
2450 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2451 // delete the dummy marker we used for visual representation while moving.
2452 // a new visual marker will show up automatically.
2457 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2458 : Drag (e, &c.time_bar_canvas_item())
2462 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2465 /** Do all the things we do when dragging the playhead to make it look as though
2466 * we have located, without actually doing the locate (because that would cause
2467 * the diskstream buffers to be refilled, which is too slow).
2470 CursorDrag::fake_locate (framepos_t t)
2472 _editor->playhead_cursor->set_position (t);
2474 Session* s = _editor->session ();
2475 if (s->timecode_transmission_suspended ()) {
2476 framepos_t const f = _editor->playhead_cursor->current_frame ();
2477 /* This is asynchronous so it will be sent "now"
2479 s->send_mmc_locate (f);
2480 /* These are synchronous and will be sent during the next
2483 s->queue_full_time_code ();
2484 s->queue_song_position_pointer ();
2487 show_verbose_cursor_time (t);
2488 _editor->UpdateAllTransportClocks (t);
2492 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2494 Drag::start_grab (event, c);
2496 _grab_zoom = _editor->samples_per_pixel;
2498 framepos_t where = _editor->canvas_event_sample (event);
2500 _editor->snap_to_with_modifier (where, event);
2502 _editor->_dragging_playhead = true;
2504 Session* s = _editor->session ();
2506 /* grab the track canvas item as well */
2508 _cursor.track_canvas_item().grab();
2511 if (_was_rolling && _stop) {
2515 if (s->is_auditioning()) {
2516 s->cancel_audition ();
2520 if (AudioEngine::instance()->connected()) {
2522 /* do this only if we're the engine is connected
2523 * because otherwise this request will never be
2524 * serviced and we'll busy wait forever. likewise,
2525 * notice if we are disconnected while waiting for the
2526 * request to be serviced.
2529 s->request_suspend_timecode_transmission ();
2530 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2531 /* twiddle our thumbs */
2536 fake_locate (where);
2540 CursorDrag::motion (GdkEvent* event, bool)
2542 framepos_t const adjusted_frame = adjusted_current_frame (event);
2543 if (adjusted_frame != last_pointer_frame()) {
2544 fake_locate (adjusted_frame);
2549 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2551 _editor->_dragging_playhead = false;
2553 _cursor.track_canvas_item().ungrab();
2555 if (!movement_occurred && _stop) {
2559 motion (event, false);
2561 Session* s = _editor->session ();
2563 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2564 _editor->_pending_locate_request = true;
2565 s->request_resume_timecode_transmission ();
2570 CursorDrag::aborted (bool)
2572 _cursor.track_canvas_item().ungrab();
2574 if (_editor->_dragging_playhead) {
2575 _editor->session()->request_resume_timecode_transmission ();
2576 _editor->_dragging_playhead = false;
2579 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2582 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2583 : RegionDrag (e, i, p, v)
2585 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2589 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2591 Drag::start_grab (event, cursor);
2593 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2594 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2596 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2600 FadeInDrag::setup_pointer_frame_offset ()
2602 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2603 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2604 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2608 FadeInDrag::motion (GdkEvent* event, bool)
2610 framecnt_t fade_length;
2612 framepos_t const pos = adjusted_current_frame (event);
2614 boost::shared_ptr<Region> region = _primary->region ();
2616 if (pos < (region->position() + 64)) {
2617 fade_length = 64; // this should be a minimum defined somewhere
2618 } else if (pos > region->last_frame()) {
2619 fade_length = region->length();
2621 fade_length = pos - region->position();
2624 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2626 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2632 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2635 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2639 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2641 if (!movement_occurred) {
2645 framecnt_t fade_length;
2647 framepos_t const pos = adjusted_current_frame (event);
2649 boost::shared_ptr<Region> region = _primary->region ();
2651 if (pos < (region->position() + 64)) {
2652 fade_length = 64; // this should be a minimum defined somewhere
2653 } else if (pos > region->last_frame()) {
2654 fade_length = region->length();
2656 fade_length = pos - region->position();
2659 _editor->begin_reversible_command (_("change fade in length"));
2661 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2663 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2669 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2670 XMLNode &before = alist->get_state();
2672 tmp->audio_region()->set_fade_in_length (fade_length);
2673 tmp->audio_region()->set_fade_in_active (true);
2675 XMLNode &after = alist->get_state();
2676 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2679 _editor->commit_reversible_command ();
2683 FadeInDrag::aborted (bool)
2685 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2686 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2692 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2696 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2697 : RegionDrag (e, i, p, v)
2699 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2703 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2705 Drag::start_grab (event, cursor);
2707 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2708 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2710 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2714 FadeOutDrag::setup_pointer_frame_offset ()
2716 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2717 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2718 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2722 FadeOutDrag::motion (GdkEvent* event, bool)
2724 framecnt_t fade_length;
2726 framepos_t const pos = adjusted_current_frame (event);
2728 boost::shared_ptr<Region> region = _primary->region ();
2730 if (pos > (region->last_frame() - 64)) {
2731 fade_length = 64; // this should really be a minimum fade defined somewhere
2733 else if (pos < region->position()) {
2734 fade_length = region->length();
2737 fade_length = region->last_frame() - pos;
2740 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2742 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2748 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2751 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2755 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2757 if (!movement_occurred) {
2761 framecnt_t fade_length;
2763 framepos_t const pos = adjusted_current_frame (event);
2765 boost::shared_ptr<Region> region = _primary->region ();
2767 if (pos > (region->last_frame() - 64)) {
2768 fade_length = 64; // this should really be a minimum fade defined somewhere
2770 else if (pos < region->position()) {
2771 fade_length = region->length();
2774 fade_length = region->last_frame() - pos;
2777 _editor->begin_reversible_command (_("change fade out length"));
2779 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2781 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2787 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2788 XMLNode &before = alist->get_state();
2790 tmp->audio_region()->set_fade_out_length (fade_length);
2791 tmp->audio_region()->set_fade_out_active (true);
2793 XMLNode &after = alist->get_state();
2794 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2797 _editor->commit_reversible_command ();
2801 FadeOutDrag::aborted (bool)
2803 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2804 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2810 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2814 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2817 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2819 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2822 _points.push_back (ArdourCanvas::Duple (0, 0));
2823 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2826 MarkerDrag::~MarkerDrag ()
2828 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2833 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2835 location = new Location (*l);
2836 markers.push_back (m);
2841 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2843 Drag::start_grab (event, cursor);
2847 Location *location = _editor->find_location_from_marker (_marker, is_start);
2848 _editor->_dragging_edit_point = true;
2850 update_item (location);
2852 // _drag_line->show();
2853 // _line->raise_to_top();
2856 show_verbose_cursor_time (location->start());
2858 show_verbose_cursor_time (location->end());
2861 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2864 case Selection::Toggle:
2865 /* we toggle on the button release */
2867 case Selection::Set:
2868 if (!_editor->selection->selected (_marker)) {
2869 _editor->selection->set (_marker);
2872 case Selection::Extend:
2874 Locations::LocationList ll;
2875 list<Marker*> to_add;
2877 _editor->selection->markers.range (s, e);
2878 s = min (_marker->position(), s);
2879 e = max (_marker->position(), e);
2882 if (e < max_framepos) {
2885 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2886 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2887 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2890 to_add.push_back (lm->start);
2893 to_add.push_back (lm->end);
2897 if (!to_add.empty()) {
2898 _editor->selection->add (to_add);
2902 case Selection::Add:
2903 _editor->selection->add (_marker);
2907 /* Set up copies for us to manipulate during the drag
2910 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2912 Location* l = _editor->find_location_from_marker (*i, is_start);
2919 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2921 /* range: check that the other end of the range isn't
2924 CopiedLocationInfo::iterator x;
2925 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2926 if (*(*x).location == *l) {
2930 if (x == _copied_locations.end()) {
2931 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2933 (*x).markers.push_back (*i);
2934 (*x).move_both = true;
2942 MarkerDrag::setup_pointer_frame_offset ()
2945 Location *location = _editor->find_location_from_marker (_marker, is_start);
2946 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2950 MarkerDrag::motion (GdkEvent* event, bool)
2952 framecnt_t f_delta = 0;
2954 bool move_both = false;
2955 Location *real_location;
2956 Location *copy_location = 0;
2958 framepos_t const newframe = adjusted_current_frame (event);
2959 framepos_t next = newframe;
2961 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2965 CopiedLocationInfo::iterator x;
2967 /* find the marker we're dragging, and compute the delta */
2969 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2971 copy_location = (*x).location;
2973 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2975 /* this marker is represented by this
2976 * CopiedLocationMarkerInfo
2979 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2984 if (real_location->is_mark()) {
2985 f_delta = newframe - copy_location->start();
2989 switch (_marker->type()) {
2990 case Marker::SessionStart:
2991 case Marker::RangeStart:
2992 case Marker::LoopStart:
2993 case Marker::PunchIn:
2994 f_delta = newframe - copy_location->start();
2997 case Marker::SessionEnd:
2998 case Marker::RangeEnd:
2999 case Marker::LoopEnd:
3000 case Marker::PunchOut:
3001 f_delta = newframe - copy_location->end();
3004 /* what kind of marker is this ? */
3013 if (x == _copied_locations.end()) {
3014 /* hmm, impossible - we didn't find the dragged marker */
3018 /* now move them all */
3020 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3022 copy_location = x->location;
3024 /* call this to find out if its the start or end */
3026 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3030 if (real_location->locked()) {
3034 if (copy_location->is_mark()) {
3038 copy_location->set_start (copy_location->start() + f_delta);
3042 framepos_t new_start = copy_location->start() + f_delta;
3043 framepos_t new_end = copy_location->end() + f_delta;
3045 if (is_start) { // start-of-range marker
3047 if (move_both || (*x).move_both) {
3048 copy_location->set_start (new_start);
3049 copy_location->set_end (new_end);
3050 } else if (new_start < copy_location->end()) {
3051 copy_location->set_start (new_start);
3052 } else if (newframe > 0) {
3053 _editor->snap_to (next, 1, true);
3054 copy_location->set_end (next);
3055 copy_location->set_start (newframe);
3058 } else { // end marker
3060 if (move_both || (*x).move_both) {
3061 copy_location->set_end (new_end);
3062 copy_location->set_start (new_start);
3063 } else if (new_end > copy_location->start()) {
3064 copy_location->set_end (new_end);
3065 } else if (newframe > 0) {
3066 _editor->snap_to (next, -1, true);
3067 copy_location->set_start (next);
3068 copy_location->set_end (newframe);
3073 update_item (copy_location);
3075 /* now lookup the actual GUI items used to display this
3076 * location and move them to wherever the copy of the location
3077 * is now. This means that the logic in ARDOUR::Location is
3078 * still enforced, even though we are not (yet) modifying
3079 * the real Location itself.
3082 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3085 lm->set_position (copy_location->start(), copy_location->end());
3090 assert (!_copied_locations.empty());
3092 show_verbose_cursor_time (newframe);
3096 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3098 if (!movement_occurred) {
3100 if (was_double_click()) {
3101 _editor->rename_marker (_marker);
3105 /* just a click, do nothing but finish
3106 off the selection process
3109 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3112 case Selection::Set:
3113 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3114 _editor->selection->set (_marker);
3118 case Selection::Toggle:
3119 /* we toggle on the button release, click only */
3120 _editor->selection->toggle (_marker);
3123 case Selection::Extend:
3124 case Selection::Add:
3131 _editor->_dragging_edit_point = false;
3133 _editor->begin_reversible_command ( _("move marker") );
3134 XMLNode &before = _editor->session()->locations()->get_state();
3136 MarkerSelection::iterator i;
3137 CopiedLocationInfo::iterator x;
3140 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3141 x != _copied_locations.end() && i != _editor->selection->markers.end();
3144 Location * location = _editor->find_location_from_marker (*i, is_start);
3148 if (location->locked()) {
3152 if (location->is_mark()) {
3153 location->set_start (((*x).location)->start());
3155 location->set (((*x).location)->start(), ((*x).location)->end());
3160 XMLNode &after = _editor->session()->locations()->get_state();
3161 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3162 _editor->commit_reversible_command ();
3166 MarkerDrag::aborted (bool)
3172 MarkerDrag::update_item (Location*)
3177 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3179 _cumulative_x_drag (0),
3180 _cumulative_y_drag (0)
3182 if (_zero_gain_fraction < 0.0) {
3183 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3186 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3188 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3194 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3196 Drag::start_grab (event, _editor->cursors()->fader);
3198 // start the grab at the center of the control point so
3199 // the point doesn't 'jump' to the mouse after the first drag
3200 _fixed_grab_x = _point->get_x();
3201 _fixed_grab_y = _point->get_y();
3203 float const fraction = 1 - (_point->get_y() / _point->line().height());
3205 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3207 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3208 event->button.x + 10, event->button.y + 10);
3210 _editor->verbose_cursor()->show ();
3212 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3214 if (!_point->can_slide ()) {
3215 _x_constrained = true;
3220 ControlPointDrag::motion (GdkEvent* event, bool)
3222 double dx = _drags->current_pointer_x() - last_pointer_x();
3223 double dy = _drags->current_pointer_y() - last_pointer_y();
3225 if (event->button.state & Keyboard::SecondaryModifier) {
3230 /* coordinate in pixels relative to the start of the region (for region-based automation)
3231 or track (for track-based automation) */
3232 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3233 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3235 // calculate zero crossing point. back off by .01 to stay on the
3236 // positive side of zero
3237 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3239 // make sure we hit zero when passing through
3240 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3244 if (_x_constrained) {
3247 if (_y_constrained) {
3251 _cumulative_x_drag = cx - _fixed_grab_x;
3252 _cumulative_y_drag = cy - _fixed_grab_y;
3256 cy = min ((double) _point->line().height(), cy);
3258 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3260 if (!_x_constrained) {
3261 _editor->snap_to_with_modifier (cx_frames, event);
3264 cx_frames = min (cx_frames, _point->line().maximum_time());
3266 float const fraction = 1.0 - (cy / _point->line().height());
3268 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3270 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3274 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3276 if (!movement_occurred) {
3280 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3281 _editor->reset_point_selection ();
3285 motion (event, false);
3288 _point->line().end_drag (_pushing, _final_index);
3289 _editor->session()->commit_reversible_command ();
3293 ControlPointDrag::aborted (bool)
3295 _point->line().reset ();
3299 ControlPointDrag::active (Editing::MouseMode m)
3301 if (m == Editing::MouseGain) {
3302 /* always active in mouse gain */
3306 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3307 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3310 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3313 _cumulative_y_drag (0)
3315 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3319 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3321 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3324 _item = &_line->grab_item ();
3326 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3327 origin, and ditto for y.
3330 double cx = event->button.x;
3331 double cy = event->button.y;
3333 _line->parent_group().canvas_to_item (cx, cy);
3335 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3340 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3341 /* no adjacent points */
3345 Drag::start_grab (event, _editor->cursors()->fader);
3347 /* store grab start in parent frame */
3352 double fraction = 1.0 - (cy / _line->height());
3354 _line->start_drag_line (before, after, fraction);
3356 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3357 event->button.x + 10, event->button.y + 10);
3359 _editor->verbose_cursor()->show ();
3363 LineDrag::motion (GdkEvent* event, bool)
3365 double dy = _drags->current_pointer_y() - last_pointer_y();
3367 if (event->button.state & Keyboard::SecondaryModifier) {
3371 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3373 _cumulative_y_drag = cy - _fixed_grab_y;
3376 cy = min ((double) _line->height(), cy);
3378 double const fraction = 1.0 - (cy / _line->height());
3381 /* we are ignoring x position for this drag, so we can just pass in anything */
3382 _line->drag_motion (0, fraction, true, false, ignored);
3384 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3388 LineDrag::finished (GdkEvent* event, bool movement_occured)
3390 if (movement_occured) {
3391 motion (event, false);
3392 _line->end_drag (false, 0);
3394 /* add a new control point on the line */
3396 AutomationTimeAxisView* atv;
3398 _line->end_drag (false, 0);
3400 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3401 framepos_t where = _editor->window_event_sample (event, 0, 0);
3402 atv->add_automation_event (event, where, event->button.y, false);
3406 _editor->session()->commit_reversible_command ();
3410 LineDrag::aborted (bool)
3415 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3418 _cumulative_x_drag (0)
3420 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3424 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3426 Drag::start_grab (event);
3428 _line = reinterpret_cast<Line*> (_item);
3431 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3433 double cx = event->button.x;
3434 double cy = event->button.y;
3436 _item->parent()->canvas_to_item (cx, cy);
3438 /* store grab start in parent frame */
3439 _region_view_grab_x = cx;
3441 _before = *(float*) _item->get_data ("position");
3443 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3445 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3449 FeatureLineDrag::motion (GdkEvent*, bool)
3451 double dx = _drags->current_pointer_x() - last_pointer_x();
3453 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3455 _cumulative_x_drag += dx;
3457 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3466 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3468 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3470 float *pos = new float;
3473 _line->set_data ("position", pos);
3479 FeatureLineDrag::finished (GdkEvent*, bool)
3481 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3482 _arv->update_transient(_before, _before);
3486 FeatureLineDrag::aborted (bool)
3491 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3493 , _vertical_only (false)
3495 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3499 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3501 Drag::start_grab (event);
3502 show_verbose_cursor_time (adjusted_current_frame (event));
3506 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3513 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3515 framepos_t grab = grab_frame ();
3516 if (Config->get_rubberbanding_snaps_to_grid ()) {
3517 _editor->snap_to_with_modifier (grab, event);
3520 /* base start and end on initial click position */
3530 if (_drags->current_pointer_y() < grab_y()) {
3531 y1 = _drags->current_pointer_y();
3534 y2 = _drags->current_pointer_y();
3539 if (start != end || y1 != y2) {
3541 double x1 = _editor->sample_to_pixel (start);
3542 double x2 = _editor->sample_to_pixel (end);
3543 const double min_dimension = 2.0;
3545 _editor->rubberband_rect->set_x0 (x1);
3546 if (_vertical_only) {
3547 /* fixed 10 pixel width */
3548 _editor->rubberband_rect->set_x1 (x1 + 10);
3551 x2 = min (x1 - min_dimension, x2);
3553 x2 = max (x1 + min_dimension, x2);
3555 _editor->rubberband_rect->set_x1 (x2);
3558 _editor->rubberband_rect->set_y0 (y1);
3560 y2 = min (y1 - min_dimension, y2);
3562 y2 = max (y1 + min_dimension, y2);
3565 _editor->rubberband_rect->set_y1 (y2);
3567 _editor->rubberband_rect->show();
3568 _editor->rubberband_rect->raise_to_top();
3570 show_verbose_cursor_time (pf);
3572 do_select_things (event, true);
3577 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3582 if (grab_frame() < last_pointer_frame()) {
3584 x2 = last_pointer_frame ();
3587 x1 = last_pointer_frame ();
3593 if (_drags->current_pointer_y() < grab_y()) {
3594 y1 = _drags->current_pointer_y();
3597 y2 = _drags->current_pointer_y();
3601 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3605 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3607 if (movement_occurred) {
3609 motion (event, false);
3610 do_select_things (event, false);
3616 bool do_deselect = true;
3617 MidiTimeAxisView* mtv;
3619 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3621 if (_editor->selection->empty()) {
3622 /* nothing selected */
3623 add_midi_region (mtv);
3624 do_deselect = false;
3628 /* do not deselect if Primary or Tertiary (toggle-select or
3629 * extend-select are pressed.
3632 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3633 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3640 _editor->rubberband_rect->hide();
3644 RubberbandSelectDrag::aborted (bool)
3646 _editor->rubberband_rect->hide ();
3649 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3650 : RegionDrag (e, i, p, v)
3652 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3656 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3658 Drag::start_grab (event, cursor);
3660 show_verbose_cursor_time (adjusted_current_frame (event));
3664 TimeFXDrag::motion (GdkEvent* event, bool)
3666 RegionView* rv = _primary;
3667 StreamView* cv = rv->get_time_axis_view().view ();
3669 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3670 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3671 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3673 framepos_t const pf = adjusted_current_frame (event);
3675 if (pf > rv->region()->position()) {
3676 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3679 show_verbose_cursor_time (pf);
3683 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3685 _primary->get_time_axis_view().hide_timestretch ();
3687 if (!movement_occurred) {
3691 if (last_pointer_frame() < _primary->region()->position()) {
3692 /* backwards drag of the left edge - not usable */
3696 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3698 float percentage = (double) newlen / (double) _primary->region()->length();
3700 #ifndef USE_RUBBERBAND
3701 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3702 if (_primary->region()->data_type() == DataType::AUDIO) {
3703 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3707 if (!_editor->get_selection().regions.empty()) {
3708 /* primary will already be included in the selection, and edit
3709 group shared editing will propagate selection across
3710 equivalent regions, so just use the current region
3714 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3715 error << _("An error occurred while executing time stretch operation") << endmsg;
3721 TimeFXDrag::aborted (bool)
3723 _primary->get_time_axis_view().hide_timestretch ();
3726 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3729 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3733 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3735 Drag::start_grab (event);
3739 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3741 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3745 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3747 if (movement_occurred && _editor->session()) {
3748 /* make sure we stop */
3749 _editor->session()->request_transport_speed (0.0);
3754 ScrubDrag::aborted (bool)
3759 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3764 , _original_pointer_time_axis (-1)
3765 , _last_pointer_time_axis (-1)
3766 , _time_selection_at_start (!_editor->get_selection().time.empty())
3768 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3770 if (_time_selection_at_start) {
3771 start_at_start = _editor->get_selection().time.start();
3772 end_at_start = _editor->get_selection().time.end_frame();
3777 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3779 if (_editor->session() == 0) {
3783 Gdk::Cursor* cursor = 0;
3785 switch (_operation) {
3786 case CreateSelection:
3787 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3792 cursor = _editor->cursors()->selector;
3793 Drag::start_grab (event, cursor);
3796 case SelectionStartTrim:
3797 if (_editor->clicked_axisview) {
3798 _editor->clicked_axisview->order_selection_trims (_item, true);
3800 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3803 case SelectionEndTrim:
3804 if (_editor->clicked_axisview) {
3805 _editor->clicked_axisview->order_selection_trims (_item, false);
3807 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3811 Drag::start_grab (event, cursor);
3814 case SelectionExtend:
3815 Drag::start_grab (event, cursor);
3819 if (_operation == SelectionMove) {
3820 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3822 show_verbose_cursor_time (adjusted_current_frame (event));
3825 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3829 SelectionDrag::setup_pointer_frame_offset ()
3831 switch (_operation) {
3832 case CreateSelection:
3833 _pointer_frame_offset = 0;
3836 case SelectionStartTrim:
3838 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3841 case SelectionEndTrim:
3842 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3845 case SelectionExtend:
3851 SelectionDrag::motion (GdkEvent* event, bool first_move)
3853 framepos_t start = 0;
3855 framecnt_t length = 0;
3856 framecnt_t distance = 0;
3858 framepos_t const pending_position = adjusted_current_frame (event);
3860 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3864 switch (_operation) {
3865 case CreateSelection:
3867 framepos_t grab = grab_frame ();
3870 grab = adjusted_current_frame (event, false);
3871 if (grab < pending_position) {
3872 _editor->snap_to (grab, -1);
3874 _editor->snap_to (grab, 1);
3878 if (pending_position < grab) {
3879 start = pending_position;
3882 end = pending_position;
3886 /* first drag: Either add to the selection
3887 or create a new selection
3894 /* adding to the selection */
3895 _editor->set_selected_track_as_side_effect (Selection::Add);
3896 _editor->clicked_selection = _editor->selection->add (start, end);
3903 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3904 _editor->set_selected_track_as_side_effect (Selection::Set);
3907 _editor->clicked_selection = _editor->selection->set (start, end);
3911 /* select all tracks within the rectangle that we've marked out so far */
3912 TrackViewList to_be_added_to_selection;
3913 TrackViewList to_be_removed_from_selection;
3914 TrackViewList& all_tracks (_editor->track_views);
3916 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3918 if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
3919 if (!(*i)->get_selected()) {
3920 to_be_added_to_selection.push_back (*i);
3923 if ((*i)->get_selected()) {
3924 to_be_removed_from_selection.push_back (*i);
3929 if (!to_be_added_to_selection.empty()) {
3930 _editor->selection->add (to_be_added_to_selection);
3933 if (!to_be_removed_from_selection.empty()) {
3934 _editor->selection->remove (to_be_removed_from_selection);
3939 case SelectionStartTrim:
3941 start = _editor->selection->time[_editor->clicked_selection].start;
3942 end = _editor->selection->time[_editor->clicked_selection].end;
3944 if (pending_position > end) {
3947 start = pending_position;
3951 case SelectionEndTrim:
3953 start = _editor->selection->time[_editor->clicked_selection].start;
3954 end = _editor->selection->time[_editor->clicked_selection].end;
3956 if (pending_position < start) {
3959 end = pending_position;
3966 start = _editor->selection->time[_editor->clicked_selection].start;
3967 end = _editor->selection->time[_editor->clicked_selection].end;
3969 length = end - start;
3970 distance = pending_position - start;
3971 start = pending_position;
3972 _editor->snap_to (start);
3974 end = start + length;
3978 case SelectionExtend:
3982 _editor->maybe_autoscroll (true, false, false);
3985 switch (_operation) {
3987 if (_time_selection_at_start) {
3988 _editor->selection->move_time (distance);
3992 _editor->selection->replace (_editor->clicked_selection, start, end);
3996 if (_operation == SelectionMove) {
3997 show_verbose_cursor_time(start);
3999 show_verbose_cursor_time(pending_position);
4004 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4006 Session* s = _editor->session();
4008 if (movement_occurred) {
4009 motion (event, false);
4010 /* XXX this is not object-oriented programming at all. ick */
4011 if (_editor->selection->time.consolidate()) {
4012 _editor->selection->TimeChanged ();
4015 /* XXX what if its a music time selection? */
4017 if ( s->get_play_range() && s->transport_rolling() ) {
4018 s->request_play_range (&_editor->selection->time, true);
4020 if (Config->get_always_play_range() && !s->transport_rolling()) {
4021 s->request_locate (_editor->get_selection().time.start());
4027 /* just a click, no pointer movement.
4030 if (_operation == SelectionExtend) {
4031 if (_time_selection_at_start) {
4032 framepos_t pos = adjusted_current_frame (event, false);
4033 framepos_t start = min (pos, start_at_start);
4034 framepos_t end = max (pos, end_at_start);
4035 _editor->selection->set (start, end);
4038 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4039 if (_editor->clicked_selection) {
4040 _editor->selection->remove (_editor->clicked_selection);
4043 if (!_editor->clicked_selection) {
4044 _editor->selection->clear_time();
4049 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4050 _editor->selection->set (_editor->clicked_axisview);
4053 if (s && s->get_play_range () && s->transport_rolling()) {
4054 s->request_stop (false, false);
4059 _editor->stop_canvas_autoscroll ();
4060 _editor->clicked_selection = 0;
4064 SelectionDrag::aborted (bool)
4069 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4074 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4076 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4077 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4078 physical_screen_height (_editor->get_window())));
4079 _drag_rect->hide ();
4081 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4082 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4086 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4088 if (_editor->session() == 0) {
4092 Gdk::Cursor* cursor = 0;
4094 if (!_editor->temp_location) {
4095 _editor->temp_location = new Location (*_editor->session());
4098 switch (_operation) {
4099 case CreateRangeMarker:
4100 case CreateTransportMarker:
4101 case CreateCDMarker:
4103 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4108 cursor = _editor->cursors()->selector;
4112 Drag::start_grab (event, cursor);
4114 show_verbose_cursor_time (adjusted_current_frame (event));
4118 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4120 framepos_t start = 0;
4122 ArdourCanvas::Rectangle *crect;
4124 switch (_operation) {
4125 case CreateRangeMarker:
4126 crect = _editor->range_bar_drag_rect;
4128 case CreateTransportMarker:
4129 crect = _editor->transport_bar_drag_rect;
4131 case CreateCDMarker:
4132 crect = _editor->cd_marker_bar_drag_rect;
4135 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4140 framepos_t const pf = adjusted_current_frame (event);
4142 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4143 framepos_t grab = grab_frame ();
4144 _editor->snap_to (grab);
4146 if (pf < grab_frame()) {
4154 /* first drag: Either add to the selection
4155 or create a new selection.
4160 _editor->temp_location->set (start, end);
4164 update_item (_editor->temp_location);
4166 //_drag_rect->raise_to_top();
4171 _editor->maybe_autoscroll (true, false, false);
4174 _editor->temp_location->set (start, end);
4176 double x1 = _editor->sample_to_pixel (start);
4177 double x2 = _editor->sample_to_pixel (end);
4181 update_item (_editor->temp_location);
4184 show_verbose_cursor_time (pf);
4189 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4191 Location * newloc = 0;
4195 if (movement_occurred) {
4196 motion (event, false);
4199 switch (_operation) {
4200 case CreateRangeMarker:
4201 case CreateCDMarker:
4203 _editor->begin_reversible_command (_("new range marker"));
4204 XMLNode &before = _editor->session()->locations()->get_state();
4205 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4206 if (_operation == CreateCDMarker) {
4207 flags = Location::IsRangeMarker | Location::IsCDMarker;
4208 _editor->cd_marker_bar_drag_rect->hide();
4211 flags = Location::IsRangeMarker;
4212 _editor->range_bar_drag_rect->hide();
4214 newloc = new Location (
4215 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4218 _editor->session()->locations()->add (newloc, true);
4219 XMLNode &after = _editor->session()->locations()->get_state();
4220 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4221 _editor->commit_reversible_command ();
4225 case CreateTransportMarker:
4226 // popup menu to pick loop or punch
4227 _editor->new_transport_marker_context_menu (&event->button, _item);
4233 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4235 if (_operation == CreateTransportMarker) {
4237 /* didn't drag, so just locate */
4239 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4241 } else if (_operation == CreateCDMarker) {
4243 /* didn't drag, but mark is already created so do
4246 } else { /* operation == CreateRangeMarker */
4252 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4254 if (end == max_framepos) {
4255 end = _editor->session()->current_end_frame ();
4258 if (start == max_framepos) {
4259 start = _editor->session()->current_start_frame ();
4262 switch (_editor->mouse_mode) {
4264 /* find the two markers on either side and then make the selection from it */
4265 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4269 /* find the two markers on either side of the click and make the range out of it */
4270 _editor->selection->set (start, end);
4279 _editor->stop_canvas_autoscroll ();
4283 RangeMarkerBarDrag::aborted (bool)
4289 RangeMarkerBarDrag::update_item (Location* location)
4291 double const x1 = _editor->sample_to_pixel (location->start());
4292 double const x2 = _editor->sample_to_pixel (location->end());
4294 _drag_rect->set_x0 (x1);
4295 _drag_rect->set_x1 (x2);
4298 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4302 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4306 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4308 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4309 Drag::start_grab (event, _editor->cursors()->zoom_out);
4312 Drag::start_grab (event, _editor->cursors()->zoom_in);
4316 show_verbose_cursor_time (adjusted_current_frame (event));
4320 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4325 framepos_t const pf = adjusted_current_frame (event);
4327 framepos_t grab = grab_frame ();
4328 _editor->snap_to_with_modifier (grab, event);
4330 /* base start and end on initial click position */
4342 _editor->zoom_rect->show();
4343 _editor->zoom_rect->raise_to_top();
4346 _editor->reposition_zoom_rect(start, end);
4348 show_verbose_cursor_time (pf);
4353 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4355 if (movement_occurred) {
4356 motion (event, false);
4358 if (grab_frame() < last_pointer_frame()) {
4359 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4361 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4364 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4365 _editor->tav_zoom_step (_zoom_out);
4367 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4371 _editor->zoom_rect->hide();
4375 MouseZoomDrag::aborted (bool)
4377 _editor->zoom_rect->hide ();
4380 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4382 , _cumulative_dx (0)
4383 , _cumulative_dy (0)
4385 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4387 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4389 _region = &_primary->region_view ();
4390 _note_height = _region->midi_stream_view()->note_height ();
4394 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4396 Drag::start_grab (event);
4398 if (!(_was_selected = _primary->selected())) {
4400 /* tertiary-click means extend selection - we'll do that on button release,
4401 so don't add it here, because otherwise we make it hard to figure
4402 out the "extend-to" range.
4405 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4408 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4411 _region->note_selected (_primary, true);
4413 _region->unique_select (_primary);
4419 /** @return Current total drag x change in frames */
4421 NoteDrag::total_dx () const
4424 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4426 /* primary note time */
4427 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4429 /* new time of the primary note in session frames */
4430 frameoffset_t st = n + dx;
4432 framepos_t const rp = _region->region()->position ();
4434 /* prevent the note being dragged earlier than the region's position */
4437 /* snap and return corresponding delta */
4438 return _region->snap_frame_to_frame (st - rp) + rp - n;
4441 /** @return Current total drag y change in note number */
4443 NoteDrag::total_dy () const
4445 MidiStreamView* msv = _region->midi_stream_view ();
4446 double const y = _region->midi_view()->y_position ();
4447 /* new current note */
4448 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4450 n = max (msv->lowest_note(), n);
4451 n = min (msv->highest_note(), n);
4452 /* and work out delta */
4453 return n - msv->y_to_note (grab_y() - y);
4457 NoteDrag::motion (GdkEvent *, bool)
4459 /* Total change in x and y since the start of the drag */
4460 frameoffset_t const dx = total_dx ();
4461 int8_t const dy = total_dy ();
4463 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4464 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4465 double const tdy = -dy * _note_height - _cumulative_dy;
4468 _cumulative_dx += tdx;
4469 _cumulative_dy += tdy;
4471 int8_t note_delta = total_dy();
4473 _region->move_selection (tdx, tdy, note_delta);
4475 /* the new note value may be the same as the old one, but we
4476 * don't know what that means because the selection may have
4477 * involved more than one note and we might be doing something
4478 * odd with them. so show the note value anyway, always.
4482 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4484 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4485 (int) floor ((double)new_note));
4487 show_verbose_cursor_text (buf);
4492 NoteDrag::finished (GdkEvent* ev, bool moved)
4495 /* no motion - select note */
4497 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4498 _editor->current_mouse_mode() == Editing::MouseDraw) {
4500 if (_was_selected) {
4501 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4503 _region->note_deselected (_primary);
4506 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4507 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4509 if (!extend && !add && _region->selection_size() > 1) {
4510 _region->unique_select (_primary);
4511 } else if (extend) {
4512 _region->note_selected (_primary, true, true);
4514 /* it was added during button press */
4519 _region->note_dropped (_primary, total_dx(), total_dy());
4524 NoteDrag::aborted (bool)
4529 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4530 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4531 : Drag (editor, atv->base_item ())
4533 , _nothing_to_drag (false)
4535 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4536 y_origin = atv->y_position();
4537 setup (atv->lines ());
4540 /** Make an AutomationRangeDrag for region gain lines */
4541 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4542 : Drag (editor, rv->get_canvas_group ())
4544 , _nothing_to_drag (false)
4546 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4548 list<boost::shared_ptr<AutomationLine> > lines;
4549 lines.push_back (rv->get_gain_line ());
4550 y_origin = rv->get_time_axis_view().y_position();
4554 /** @param lines AutomationLines to drag.
4555 * @param offset Offset from the session start to the points in the AutomationLines.
4558 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4560 /* find the lines that overlap the ranges being dragged */
4561 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4562 while (i != lines.end ()) {
4563 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4566 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4568 /* check this range against all the AudioRanges that we are using */
4569 list<AudioRange>::const_iterator k = _ranges.begin ();
4570 while (k != _ranges.end()) {
4571 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4577 /* add it to our list if it overlaps at all */
4578 if (k != _ranges.end()) {
4583 _lines.push_back (n);
4589 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4593 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4595 return 1.0 - ((global_y - y_origin) / line->height());
4599 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4601 Drag::start_grab (event, cursor);
4603 /* Get line states before we start changing things */
4604 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4605 i->state = &i->line->get_state ();
4606 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4609 if (_ranges.empty()) {
4611 /* No selected time ranges: drag all points */
4612 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4613 uint32_t const N = i->line->npoints ();
4614 for (uint32_t j = 0; j < N; ++j) {
4615 i->points.push_back (i->line->nth (j));
4621 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4623 framecnt_t const half = (i->start + i->end) / 2;
4625 /* find the line that this audio range starts in */
4626 list<Line>::iterator j = _lines.begin();
4627 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4631 if (j != _lines.end()) {
4632 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4634 /* j is the line that this audio range starts in; fade into it;
4635 64 samples length plucked out of thin air.
4638 framepos_t a = i->start + 64;
4643 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4644 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4646 the_list->add (p, the_list->eval (p));
4647 the_list->add (q, the_list->eval (q));
4650 /* same thing for the end */
4653 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4657 if (j != _lines.end()) {
4658 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4660 /* j is the line that this audio range starts in; fade out of it;
4661 64 samples length plucked out of thin air.
4664 framepos_t b = i->end - 64;
4669 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4670 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4672 the_list->add (p, the_list->eval (p));
4673 the_list->add (q, the_list->eval (q));
4677 _nothing_to_drag = true;
4679 /* Find all the points that should be dragged and put them in the relevant
4680 points lists in the Line structs.
4683 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4685 uint32_t const N = i->line->npoints ();
4686 for (uint32_t j = 0; j < N; ++j) {
4688 /* here's a control point on this line */
4689 ControlPoint* p = i->line->nth (j);
4690 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4692 /* see if it's inside a range */
4693 list<AudioRange>::const_iterator k = _ranges.begin ();
4694 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4698 if (k != _ranges.end()) {
4699 /* dragging this point */
4700 _nothing_to_drag = false;
4701 i->points.push_back (p);
4707 if (_nothing_to_drag) {
4711 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4712 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4717 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4719 if (_nothing_to_drag) {
4723 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4724 float const f = y_fraction (l->line, _drags->current_pointer_y());
4725 /* we are ignoring x position for this drag, so we can just pass in anything */
4727 l->line->drag_motion (0, f, true, false, ignored);
4728 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4733 AutomationRangeDrag::finished (GdkEvent* event, bool)
4735 if (_nothing_to_drag) {
4739 motion (event, false);
4740 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4741 i->line->end_drag (false, 0);
4744 _editor->session()->commit_reversible_command ();
4748 AutomationRangeDrag::aborted (bool)
4750 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4755 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4758 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4759 layer = v->region()->layer ();
4760 initial_y = v->get_canvas_group()->position().y;
4761 initial_playlist = v->region()->playlist ();
4762 initial_position = v->region()->position ();
4763 initial_end = v->region()->position () + v->region()->length ();
4766 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4767 : Drag (e, i->canvas_item ())
4770 , _cumulative_dx (0)
4772 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4773 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4778 PatchChangeDrag::motion (GdkEvent* ev, bool)
4780 framepos_t f = adjusted_current_frame (ev);
4781 boost::shared_ptr<Region> r = _region_view->region ();
4782 f = max (f, r->position ());
4783 f = min (f, r->last_frame ());
4785 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4786 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4787 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4788 _cumulative_dx = dxu;
4792 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4794 if (!movement_occurred) {
4798 boost::shared_ptr<Region> r (_region_view->region ());
4799 framepos_t f = adjusted_current_frame (ev);
4800 f = max (f, r->position ());
4801 f = min (f, r->last_frame ());
4803 _region_view->move_patch_change (
4805 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4810 PatchChangeDrag::aborted (bool)
4812 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4816 PatchChangeDrag::setup_pointer_frame_offset ()
4818 boost::shared_ptr<Region> region = _region_view->region ();
4819 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4822 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4823 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4830 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4832 framepos_t const p = _region_view->region()->position ();
4833 double const y = _region_view->midi_view()->y_position ();
4835 x1 = max ((framepos_t) 0, x1 - p);
4836 x2 = max ((framepos_t) 0, x2 - p);
4837 y1 = max (0.0, y1 - y);
4838 y2 = max (0.0, y2 - y);
4840 _region_view->update_drag_selection (
4841 _editor->sample_to_pixel (x1),
4842 _editor->sample_to_pixel (x2),
4845 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4850 MidiRubberbandSelectDrag::deselect_things ()
4855 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4856 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4859 _vertical_only = true;
4863 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4865 double const y = _region_view->midi_view()->y_position ();
4867 y1 = max (0.0, y1 - y);
4868 y2 = max (0.0, y2 - y);
4870 _region_view->update_vertical_drag_selection (
4873 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4878 MidiVerticalSelectDrag::deselect_things ()
4883 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4884 : RubberbandSelectDrag (e, i)
4890 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4892 if (drag_in_progress) {
4893 /* We just want to select things at the end of the drag, not during it */
4897 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4899 _editor->begin_reversible_command (_("rubberband selection"));
4900 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4901 _editor->commit_reversible_command ();
4905 EditorRubberbandSelectDrag::deselect_things ()
4907 if (!getenv("ARDOUR_SAE")) {
4908 _editor->selection->clear_tracks();
4910 _editor->selection->clear_regions();
4911 _editor->selection->clear_points ();
4912 _editor->selection->clear_lines ();
4915 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4923 NoteCreateDrag::~NoteCreateDrag ()
4929 NoteCreateDrag::grid_frames (framepos_t t) const
4932 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4937 return _region_view->region_beats_to_region_frames (grid_beats);
4941 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4943 Drag::start_grab (event, cursor);
4945 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4947 framepos_t pf = _drags->current_pointer_frame ();
4948 framecnt_t const g = grid_frames (pf);
4950 /* Hack so that we always snap to the note that we are over, instead of snapping
4951 to the next one if we're more than halfway through the one we're over.
4953 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4957 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4959 MidiStreamView* sv = _region_view->midi_stream_view ();
4960 double const x = _editor->sample_to_pixel (_note[0]);
4961 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4963 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4964 _drag_rect->set_outline_all ();
4965 _drag_rect->set_outline_color (0xffffff99);
4966 _drag_rect->set_fill_color (0xffffff66);
4970 NoteCreateDrag::motion (GdkEvent* event, bool)
4972 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4973 double const x = _editor->sample_to_pixel (_note[1]);
4974 if (_note[1] > _note[0]) {
4975 _drag_rect->set_x1 (x);
4977 _drag_rect->set_x0 (x);
4982 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4984 if (!had_movement) {
4988 framepos_t const start = min (_note[0], _note[1]);
4989 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
4991 framecnt_t const g = grid_frames (start);
4992 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4994 if (_editor->snap_mode() == SnapNormal && length < g) {
4995 length = g - one_tick;
4998 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5000 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5004 NoteCreateDrag::y_to_region (double y) const
5007 _region_view->get_canvas_group()->canvas_to_item (x, y);
5012 NoteCreateDrag::aborted (bool)
5017 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5022 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5026 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5028 Drag::start_grab (event, cursor);
5032 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5038 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5041 distance = _drags->current_pointer_x() - grab_x();
5042 len = ar->fade_in()->back()->when;
5044 distance = grab_x() - _drags->current_pointer_x();
5045 len = ar->fade_out()->back()->when;
5048 /* how long should it be ? */
5050 new_length = len + _editor->pixel_to_sample (distance);
5052 /* now check with the region that this is legal */
5054 new_length = ar->verify_xfade_bounds (new_length, start);
5057 arv->reset_fade_in_shape_width (ar, new_length);
5059 arv->reset_fade_out_shape_width (ar, new_length);
5064 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5070 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5073 distance = _drags->current_pointer_x() - grab_x();
5074 len = ar->fade_in()->back()->when;
5076 distance = grab_x() - _drags->current_pointer_x();
5077 len = ar->fade_out()->back()->when;
5080 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5082 _editor->begin_reversible_command ("xfade trim");
5083 ar->playlist()->clear_owned_changes ();
5086 ar->set_fade_in_length (new_length);
5088 ar->set_fade_out_length (new_length);
5091 /* Adjusting the xfade may affect other regions in the playlist, so we need
5092 to get undo Commands from the whole playlist rather than just the
5096 vector<Command*> cmds;
5097 ar->playlist()->rdiff (cmds);
5098 _editor->session()->add_commands (cmds);
5099 _editor->commit_reversible_command ();
5104 CrossfadeEdgeDrag::aborted (bool)
5107 arv->redraw_start_xfade ();
5109 arv->redraw_end_xfade ();