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"
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/audioregion.h"
33 #include "ardour/dB.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/operations.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/session.h"
42 #include "audio_region_view.h"
43 #include "midi_region_view.h"
44 #include "ardour_ui.h"
45 #include "gui_thread.h"
46 #include "control_point.h"
48 #include "region_gain_line.h"
49 #include "editor_drag.h"
50 #include "audio_time_axis.h"
51 #include "midi_time_axis.h"
52 #include "canvas-note.h"
53 #include "selection.h"
54 #include "midi_selection.h"
55 #include "automation_time_axis.h"
57 #include "editor_cursors.h"
58 #include "mouse_cursors.h"
59 #include "verbose_cursor.h"
62 using namespace ARDOUR;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67 using namespace ArdourCanvas;
69 using Gtkmm2ext::Keyboard;
71 double ControlPointDrag::_zero_gain_fraction = -1.0;
73 DragManager::DragManager (Editor* e)
76 , _current_pointer_frame (0)
80 DragManager::~DragManager ()
85 /** Call abort for each active drag */
91 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
96 if (!_drags.empty ()) {
97 _editor->set_follow_playhead (_old_follow_playhead, false);
106 DragManager::add (Drag* d)
108 d->set_manager (this);
109 _drags.push_back (d);
113 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
115 d->set_manager (this);
116 _drags.push_back (d);
121 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
123 /* Prevent follow playhead during the drag to be nice to the user */
124 _old_follow_playhead = _editor->follow_playhead ();
125 _editor->set_follow_playhead (false);
127 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
129 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
130 (*i)->start_grab (e, c);
134 /** Call end_grab for each active drag.
135 * @return true if any drag reported movement having occurred.
138 DragManager::end_grab (GdkEvent* e)
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->end_grab (e);
155 _editor->set_follow_playhead (_old_follow_playhead, false);
161 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
165 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
167 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
168 bool const t = (*i)->motion_handler (e, from_autoscroll);
179 DragManager::have_item (ArdourCanvas::Item* i) const
181 list<Drag*>::const_iterator j = _drags.begin ();
182 while (j != _drags.end() && (*j)->item () != i) {
186 return j != _drags.end ();
189 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
192 , _pointer_frame_offset (0)
193 , _move_threshold_passed (false)
194 , _raw_grab_frame (0)
196 , _last_pointer_frame (0)
202 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
208 cursor = _editor->which_grabber_cursor ();
211 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
215 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
218 cursor = _editor->which_grabber_cursor ();
221 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
223 if (Keyboard::is_button2_event (&event->button)) {
224 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
225 _y_constrained = true;
226 _x_constrained = false;
228 _y_constrained = false;
229 _x_constrained = true;
232 _x_constrained = false;
233 _y_constrained = false;
236 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
237 setup_pointer_frame_offset ();
238 _grab_frame = adjusted_frame (_raw_grab_frame, event);
239 _last_pointer_frame = _grab_frame;
240 _last_pointer_x = _grab_x;
241 _last_pointer_y = _grab_y;
243 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
247 if (_editor->session() && _editor->session()->transport_rolling()) {
250 _was_rolling = false;
253 switch (_editor->snap_type()) {
254 case SnapToRegionStart:
255 case SnapToRegionEnd:
256 case SnapToRegionSync:
257 case SnapToRegionBoundary:
258 _editor->build_region_boundary_cache ();
265 /** Call to end a drag `successfully'. Ungrabs item and calls
266 * subclass' finished() method.
268 * @param event GDK event, or 0.
269 * @return true if some movement occurred, otherwise false.
272 Drag::end_grab (GdkEvent* event)
274 _editor->stop_canvas_autoscroll ();
276 _item->ungrab (event ? event->button.time : 0);
278 finished (event, _move_threshold_passed);
280 _editor->verbose_cursor()->hide ();
282 return _move_threshold_passed;
286 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
290 if (f > _pointer_frame_offset) {
291 pos = f - _pointer_frame_offset;
295 _editor->snap_to_with_modifier (pos, event);
302 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
304 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
308 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
310 /* check to see if we have moved in any way that matters since the last motion event */
311 if (_move_threshold_passed &&
312 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
313 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
317 pair<framecnt_t, int> const threshold = move_threshold ();
319 bool const old_move_threshold_passed = _move_threshold_passed;
321 if (!from_autoscroll && !_move_threshold_passed) {
323 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
324 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
326 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
329 if (active (_editor->mouse_mode) && _move_threshold_passed) {
331 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
332 if (!from_autoscroll) {
333 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
336 motion (event, _move_threshold_passed != old_move_threshold_passed);
338 _last_pointer_x = _drags->current_pointer_x ();
339 _last_pointer_y = _drags->current_pointer_y ();
340 _last_pointer_frame = adjusted_current_frame (event);
348 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
356 aborted (_move_threshold_passed);
358 _editor->stop_canvas_autoscroll ();
359 _editor->verbose_cursor()->hide ();
363 Drag::show_verbose_cursor_time (framepos_t frame)
365 _editor->verbose_cursor()->set_time (
367 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
368 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
371 _editor->verbose_cursor()->show ();
375 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
377 _editor->verbose_cursor()->show (xoffset);
379 _editor->verbose_cursor()->set_duration (
381 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
382 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
387 Drag::show_verbose_cursor_text (string const & text)
389 _editor->verbose_cursor()->show ();
391 _editor->verbose_cursor()->set (
393 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
394 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
399 struct EditorOrderTimeAxisViewSorter {
400 bool operator() (TimeAxisView* a, TimeAxisView* b) {
401 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
402 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
404 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
408 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
412 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
414 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
415 as some of the regions we are dragging may be on such tracks.
418 TrackViewList track_views = _editor->track_views;
419 track_views.sort (EditorOrderTimeAxisViewSorter ());
421 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
422 _time_axis_views.push_back (*i);
424 TimeAxisView::Children children_list = (*i)->get_child_list ();
425 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
426 _time_axis_views.push_back (j->get());
430 /* the list of views can be empty at this point if this is a region list-insert drag
433 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
434 _views.push_back (DraggingView (*i, this));
437 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
441 RegionDrag::region_going_away (RegionView* v)
443 list<DraggingView>::iterator i = _views.begin ();
444 while (i != _views.end() && i->view != v) {
448 if (i != _views.end()) {
453 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
454 * or -1 if it is not found.
457 RegionDrag::find_time_axis_view (TimeAxisView* t) const
460 int const N = _time_axis_views.size ();
461 while (i < N && _time_axis_views[i] != t) {
472 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
473 : RegionDrag (e, i, p, v),
482 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
484 Drag::start_grab (event, cursor);
486 show_verbose_cursor_time (_last_frame_position);
488 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
489 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
490 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
494 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
496 /* compute the amount of pointer motion in frames, and where
497 the region would be if we moved it by that much.
499 *pending_region_position = adjusted_current_frame (event);
501 framepos_t sync_frame;
502 framecnt_t sync_offset;
505 sync_offset = _primary->region()->sync_offset (sync_dir);
507 /* we don't handle a sync point that lies before zero.
509 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
511 sync_frame = *pending_region_position + (sync_dir*sync_offset);
513 _editor->snap_to_with_modifier (sync_frame, event);
515 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
518 *pending_region_position = _last_frame_position;
521 if (*pending_region_position > max_framepos - _primary->region()->length()) {
522 *pending_region_position = _last_frame_position;
527 /* in locked edit mode, reverse the usual meaning of _x_constrained */
528 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
530 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
532 /* x movement since last time (in pixels) */
533 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
535 /* total x movement */
536 framecnt_t total_dx = *pending_region_position;
537 if (regions_came_from_canvas()) {
538 total_dx = total_dx - grab_frame ();
541 /* check that no regions have gone off the start of the session */
542 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
543 if ((i->view->region()->position() + total_dx) < 0) {
545 *pending_region_position = _last_frame_position;
550 _last_frame_position = *pending_region_position;
557 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
559 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
560 int const n = i->time_axis_view + delta_track;
561 if (n < 0 || n >= int (_time_axis_views.size())) {
562 /* off the top or bottom track */
566 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
567 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
568 /* not a track, or the wrong type */
572 int const l = i->layer + delta_layer;
573 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
574 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
575 If it has, the layers will be munged later anyway, so it's ok.
581 /* all regions being dragged are ok with this change */
586 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
588 assert (!_views.empty ());
590 /* Find the TimeAxisView that the pointer is now over */
591 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
593 /* Bail early if we're not over a track */
594 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
595 if (!rtv || !rtv->is_track()) {
596 _editor->verbose_cursor()->hide ();
600 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
602 /* Here's the current pointer position in terms of time axis view and layer */
603 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
604 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
606 /* Work out the change in x */
607 framepos_t pending_region_position;
608 double const x_delta = compute_x_delta (event, &pending_region_position);
610 /* Work out the change in y */
611 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
612 int delta_layer = current_pointer_layer - _last_pointer_layer;
614 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
615 /* this y movement is not allowed, so do no y movement this time */
616 delta_time_axis_view = 0;
620 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
621 /* haven't reached next snap point, and we're not switching
622 trackviews nor layers. nothing to do.
627 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
629 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
631 RegionView* rv = i->view;
633 if (rv->region()->locked()) {
639 rv->get_time_axis_view().hide_dependent_views (*rv);
641 /* Reparent to a non scrolling group so that we can keep the
642 region selection above all time axis views.
643 Reparenting means that we will have to move the region view
644 later, as the two parent groups have different coordinates.
647 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
649 rv->fake_set_opaque (true);
651 if (!rv->get_time_axis_view().hidden()) {
652 /* the track that this region view is on is hidden, so hide the region too */
653 rv->get_canvas_group()->hide ();
657 /* If we have moved tracks, we'll fudge the layer delta so that the
658 region gets moved back onto layer 0 on its new track; this avoids
659 confusion when dragging regions from non-zero layers onto different
662 int this_delta_layer = delta_layer;
663 if (delta_time_axis_view != 0) {
664 this_delta_layer = - i->layer;
667 /* The TimeAxisView that this region is now on */
668 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
671 rv->set_height (tv->view()->child_height ());
673 /* Update show/hidden status as the region view may have come from a hidden track,
674 or have moved to one.
677 rv->get_canvas_group()->hide ();
679 rv->get_canvas_group()->show ();
682 /* Update the DraggingView */
683 i->time_axis_view += delta_time_axis_view;
684 i->layer += this_delta_layer;
687 _editor->mouse_brush_insert_region (rv, pending_region_position);
692 /* Get the y coordinate of the top of the track that this region is now on */
693 tv->canvas_display()->i2w (x, y);
694 y += _editor->get_trackview_group_vertical_offset();
696 /* And adjust for the layer that it should be on */
697 StreamView* cv = tv->view ();
698 if (cv->layer_display() == Stacked) {
699 y += (cv->layers() - i->layer - 1) * cv->child_height ();
702 /* Now move the region view */
703 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
706 } /* foreach region */
708 _total_x_delta += x_delta;
711 _editor->cursor_group->raise_to_top();
714 if (x_delta != 0 && !_brushing) {
715 show_verbose_cursor_time (_last_frame_position);
718 _last_pointer_time_axis_view += delta_time_axis_view;
719 _last_pointer_layer += delta_layer;
723 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
725 if (_copy && first_move) {
727 /* duplicate the regionview(s) and region(s) */
729 list<DraggingView> new_regionviews;
731 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
733 RegionView* rv = i->view;
734 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
735 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
737 const boost::shared_ptr<const Region> original = rv->region();
738 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
739 region_copy->set_position (original->position());
743 boost::shared_ptr<AudioRegion> audioregion_copy
744 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
746 nrv = new AudioRegionView (*arv, audioregion_copy);
748 boost::shared_ptr<MidiRegion> midiregion_copy
749 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
750 nrv = new MidiRegionView (*mrv, midiregion_copy);
755 nrv->get_canvas_group()->show ();
756 new_regionviews.push_back (DraggingView (nrv, this));
758 /* swap _primary to the copy */
760 if (rv == _primary) {
764 /* ..and deselect the one we copied */
766 rv->set_selected (false);
769 if (!new_regionviews.empty()) {
771 /* reflect the fact that we are dragging the copies */
773 _views = new_regionviews;
775 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
778 sync the canvas to what we think is its current state
779 without it, the canvas seems to
780 "forget" to update properly after the upcoming reparent()
781 ..only if the mouse is in rapid motion at the time of the grab.
782 something to do with regionview creation taking so long?
784 _editor->update_canvas_now();
788 RegionMotionDrag::motion (event, first_move);
792 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
794 if (!movement_occurred) {
799 /* reverse this here so that we have the correct logic to finalize
803 if (Config->get_edit_mode() == Lock) {
804 _x_constrained = !_x_constrained;
807 assert (!_views.empty ());
809 /* We might have hidden region views so that they weren't visible during the drag
810 (when they have been reparented). Now everything can be shown again, as region
811 views are back in their track parent groups.
813 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
814 i->view->get_canvas_group()->show ();
817 bool const changed_position = (_last_frame_position != _primary->region()->position());
818 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
819 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
821 _editor->update_canvas_now ();
843 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
845 RegionSelection new_views;
846 PlaylistSet modified_playlists;
847 list<RegionView*> views_to_delete;
850 /* all changes were made during motion event handlers */
852 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
856 _editor->commit_reversible_command ();
860 if (_x_constrained) {
861 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
863 _editor->begin_reversible_command (Operations::region_copy);
866 /* insert the regions into their new playlists */
867 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
869 if (i->view->region()->locked()) {
875 if (changed_position && !_x_constrained) {
876 where = i->view->region()->position() - drag_delta;
878 where = i->view->region()->position();
881 RegionView* new_view = insert_region_into_playlist (
882 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
889 new_views.push_back (new_view);
891 /* we don't need the copied RegionView any more */
892 views_to_delete.push_back (i->view);
895 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
896 because when views are deleted they are automagically removed from _views, which messes
899 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
903 /* If we've created new regions either by copying or moving
904 to a new track, we want to replace the old selection with the new ones
907 if (new_views.size() > 0) {
908 _editor->selection->set (new_views);
911 /* write commands for the accumulated diffs for all our modified playlists */
912 add_stateful_diff_commands_for_playlists (modified_playlists);
914 _editor->commit_reversible_command ();
918 RegionMoveDrag::finished_no_copy (
919 bool const changed_position,
920 bool const changed_tracks,
921 framecnt_t const drag_delta
924 RegionSelection new_views;
925 PlaylistSet modified_playlists;
926 PlaylistSet frozen_playlists;
929 /* all changes were made during motion event handlers */
930 _editor->commit_reversible_command ();
934 if (_x_constrained) {
935 _editor->begin_reversible_command (_("fixed time region drag"));
937 _editor->begin_reversible_command (Operations::region_drag);
940 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
942 RegionView* rv = i->view;
944 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
945 layer_t const dest_layer = i->layer;
947 if (rv->region()->locked()) {
954 if (changed_position && !_x_constrained) {
955 where = rv->region()->position() - drag_delta;
957 where = rv->region()->position();
960 if (changed_tracks) {
962 /* insert into new playlist */
964 RegionView* new_view = insert_region_into_playlist (
965 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
973 new_views.push_back (new_view);
975 /* remove from old playlist */
977 /* the region that used to be in the old playlist is not
978 moved to the new one - we use a copy of it. as a result,
979 any existing editor for the region should no longer be
982 rv->hide_region_editor();
983 rv->fake_set_opaque (false);
985 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
989 rv->region()->clear_changes ();
992 motion on the same track. plonk the previously reparented region
993 back to its original canvas group (its streamview).
994 No need to do anything for copies as they are fake regions which will be deleted.
997 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
998 rv->get_canvas_group()->property_y() = i->initial_y;
999 rv->get_time_axis_view().reveal_dependent_views (*rv);
1001 /* just change the model */
1003 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1005 if (dest_rtv->view()->layer_display() == Stacked) {
1006 rv->region()->set_layer (dest_layer);
1007 rv->region()->set_pending_explicit_relayer (true);
1010 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1012 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1015 playlist->freeze ();
1018 /* this movement may result in a crossfade being modified, so we need to get undo
1019 data from the playlist as well as the region.
1022 r = modified_playlists.insert (playlist);
1024 playlist->clear_changes ();
1027 rv->region()->set_position (where);
1029 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1032 if (changed_tracks) {
1034 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1035 was selected in all of them, then removing it from a playlist will have removed all
1036 trace of it from _views (i.e. there were N regions selected, we removed 1,
1037 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1038 corresponding regionview, and _views is now empty).
1040 This could have invalidated any and all iterators into _views.
1042 The heuristic we use here is: if the region selection is empty, break out of the loop
1043 here. if the region selection is not empty, then restart the loop because we know that
1044 we must have removed at least the region(view) we've just been working on as well as any
1045 that we processed on previous iterations.
1047 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1048 we can just iterate.
1052 if (_views.empty()) {
1063 /* If we've created new regions either by copying or moving
1064 to a new track, we want to replace the old selection with the new ones
1067 if (new_views.size() > 0) {
1068 _editor->selection->set (new_views);
1071 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1075 /* write commands for the accumulated diffs for all our modified playlists */
1076 add_stateful_diff_commands_for_playlists (modified_playlists);
1078 _editor->commit_reversible_command ();
1081 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1082 * @param region Region to remove.
1083 * @param playlist playlist To remove from.
1084 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1085 * that clear_changes () is only called once per playlist.
1088 RegionMoveDrag::remove_region_from_playlist (
1089 boost::shared_ptr<Region> region,
1090 boost::shared_ptr<Playlist> playlist,
1091 PlaylistSet& modified_playlists
1094 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1097 playlist->clear_changes ();
1100 playlist->remove_region (region);
1104 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1105 * clearing the playlist's diff history first if necessary.
1106 * @param region Region to insert.
1107 * @param dest_rtv Destination RouteTimeAxisView.
1108 * @param dest_layer Destination layer.
1109 * @param where Destination position.
1110 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1111 * that clear_changes () is only called once per playlist.
1112 * @return New RegionView, or 0 if no insert was performed.
1115 RegionMoveDrag::insert_region_into_playlist (
1116 boost::shared_ptr<Region> region,
1117 RouteTimeAxisView* dest_rtv,
1120 PlaylistSet& modified_playlists
1123 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1124 if (!dest_playlist) {
1128 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1129 _new_region_view = 0;
1130 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1132 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1133 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1135 dest_playlist->clear_changes ();
1138 dest_playlist->add_region (region, where);
1140 if (dest_rtv->view()->layer_display() == Stacked) {
1141 region->set_layer (dest_layer);
1142 region->set_pending_explicit_relayer (true);
1147 assert (_new_region_view);
1149 return _new_region_view;
1153 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1155 _new_region_view = rv;
1159 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1161 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1162 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1164 _editor->session()->add_command (c);
1173 RegionMoveDrag::aborted (bool movement_occurred)
1177 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1184 RegionMotionDrag::aborted (movement_occurred);
1189 RegionMotionDrag::aborted (bool)
1191 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1192 RegionView* rv = i->view;
1193 TimeAxisView* tv = &(rv->get_time_axis_view ());
1194 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1196 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1197 rv->get_canvas_group()->property_y() = 0;
1198 rv->get_time_axis_view().reveal_dependent_views (*rv);
1199 rv->fake_set_opaque (false);
1200 rv->move (-_total_x_delta, 0);
1201 rv->set_height (rtv->view()->child_height ());
1204 _editor->update_canvas_now ();
1207 /** @param b true to brush, otherwise false.
1208 * @param c true to make copies of the regions being moved, otherwise false.
1210 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1211 : RegionMotionDrag (e, i, p, v, b),
1214 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1217 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1218 if (rtv && rtv->is_track()) {
1219 speed = rtv->track()->speed ();
1222 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1226 RegionMoveDrag::setup_pointer_frame_offset ()
1228 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1231 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1232 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1234 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1236 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1237 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1239 _primary = v->view()->create_region_view (r, false, false);
1241 _primary->get_canvas_group()->show ();
1242 _primary->set_position (pos, 0);
1243 _views.push_back (DraggingView (_primary, this));
1245 _last_frame_position = pos;
1247 _item = _primary->get_canvas_group ();
1251 RegionInsertDrag::finished (GdkEvent *, bool)
1253 _editor->update_canvas_now ();
1255 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1257 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1258 _primary->get_canvas_group()->property_y() = 0;
1260 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1262 _editor->begin_reversible_command (Operations::insert_region);
1263 playlist->clear_changes ();
1264 playlist->add_region (_primary->region (), _last_frame_position);
1265 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1266 _editor->commit_reversible_command ();
1274 RegionInsertDrag::aborted (bool)
1281 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1282 : RegionMoveDrag (e, i, p, v, false, false)
1284 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1287 struct RegionSelectionByPosition {
1288 bool operator() (RegionView*a, RegionView* b) {
1289 return a->region()->position () < b->region()->position();
1294 RegionSpliceDrag::motion (GdkEvent* event, bool)
1296 /* Which trackview is this ? */
1298 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1299 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1301 /* The region motion is only processed if the pointer is over
1305 if (!tv || !tv->is_track()) {
1306 /* To make sure we hide the verbose canvas cursor when the mouse is
1307 not held over and audiotrack.
1309 _editor->verbose_cursor()->hide ();
1315 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1321 RegionSelection copy (_editor->selection->regions);
1323 RegionSelectionByPosition cmp;
1326 framepos_t const pf = adjusted_current_frame (event);
1328 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1330 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1336 boost::shared_ptr<Playlist> playlist;
1338 if ((playlist = atv->playlist()) == 0) {
1342 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1347 if (pf < (*i)->region()->last_frame() + 1) {
1351 if (pf > (*i)->region()->first_frame()) {
1357 playlist->shuffle ((*i)->region(), dir);
1362 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1364 RegionMoveDrag::finished (event, movement_occurred);
1368 RegionSpliceDrag::aborted (bool)
1373 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1375 _view (dynamic_cast<MidiTimeAxisView*> (v))
1377 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1383 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1387 _view->playlist()->freeze ();
1390 framepos_t const f = adjusted_current_frame (event);
1391 if (f < grab_frame()) {
1392 _region->set_position (f);
1395 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1396 so that if this region is duplicated, its duplicate starts on
1397 a snap point rather than 1 frame after a snap point. Otherwise things get
1398 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1399 place snapped notes at the start of the region.
1402 framecnt_t const len = abs (f - grab_frame () - 1);
1403 _region->set_length (len < 1 ? 1 : len);
1409 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1411 if (!movement_occurred) {
1414 _view->playlist()->thaw ();
1418 _editor->commit_reversible_command ();
1423 RegionCreateDrag::add_region ()
1425 if (_editor->session()) {
1426 const TempoMap& map (_editor->session()->tempo_map());
1427 framecnt_t pos = grab_frame();
1428 const Meter& m = map.meter_at (pos);
1429 /* not that the frame rate used here can be affected by pull up/down which
1432 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1433 _region = _view->add_region (grab_frame(), len, false);
1438 RegionCreateDrag::aborted (bool)
1441 _view->playlist()->thaw ();
1447 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1451 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1455 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1457 Gdk::Cursor* cursor;
1458 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1459 float x_fraction = cnote->mouse_x_fraction ();
1461 if (x_fraction > 0.0 && x_fraction < 0.25) {
1462 cursor = _editor->cursors()->left_side_trim;
1464 cursor = _editor->cursors()->right_side_trim;
1467 Drag::start_grab (event, cursor);
1469 region = &cnote->region_view();
1471 double const region_start = region->get_position_pixels();
1472 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1474 if (grab_x() <= middle_point) {
1475 cursor = _editor->cursors()->left_side_trim;
1478 cursor = _editor->cursors()->right_side_trim;
1482 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1484 if (event->motion.state & Keyboard::PrimaryModifier) {
1490 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1492 if (ms.size() > 1) {
1493 /* has to be relative, may make no sense otherwise */
1497 /* select this note; if it is already selected, preserve the existing selection,
1498 otherwise make this note the only one selected.
1500 region->note_selected (cnote, cnote->selected ());
1502 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1503 MidiRegionSelection::iterator next;
1506 (*r)->begin_resizing (at_front);
1512 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1514 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1515 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1516 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1521 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1523 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1524 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1525 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1530 NoteResizeDrag::aborted (bool)
1532 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1533 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1534 (*r)->abort_resizing ();
1538 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1541 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1545 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1551 RegionGainDrag::finished (GdkEvent *, bool)
1557 RegionGainDrag::aborted (bool)
1562 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1563 : RegionDrag (e, i, p, v)
1565 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1569 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1572 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1573 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1575 if (tv && tv->is_track()) {
1576 speed = tv->track()->speed();
1579 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1580 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1581 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1583 framepos_t const pf = adjusted_current_frame (event);
1585 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1586 /* Move the contents of the region around without changing the region bounds */
1587 _operation = ContentsTrim;
1588 Drag::start_grab (event, _editor->cursors()->trimmer);
1590 /* These will get overridden for a point trim.*/
1591 if (pf < (region_start + region_length/2)) {
1592 /* closer to front */
1593 _operation = StartTrim;
1594 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1597 _operation = EndTrim;
1598 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1602 switch (_operation) {
1604 show_verbose_cursor_time (region_start);
1605 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1606 i->view->trim_front_starting ();
1610 show_verbose_cursor_time (region_end);
1613 show_verbose_cursor_time (pf);
1617 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1618 i->view->region()->suspend_property_changes ();
1623 TrimDrag::motion (GdkEvent* event, bool first_move)
1625 RegionView* rv = _primary;
1628 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1629 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1630 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1632 if (tv && tv->is_track()) {
1633 speed = tv->track()->speed();
1636 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1642 switch (_operation) {
1644 trim_type = "Region start trim";
1647 trim_type = "Region end trim";
1650 trim_type = "Region content trim";
1654 _editor->begin_reversible_command (trim_type);
1656 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1657 RegionView* rv = i->view;
1658 rv->fake_set_opaque (false);
1659 rv->enable_display (false);
1660 rv->region()->playlist()->clear_owned_changes ();
1662 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1665 arv->temporarily_hide_envelope ();
1668 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1669 insert_result = _editor->motion_frozen_playlists.insert (pl);
1671 if (insert_result.second) {
1677 bool non_overlap_trim = false;
1679 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1680 non_overlap_trim = true;
1683 switch (_operation) {
1685 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1686 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1691 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1692 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1698 bool swap_direction = false;
1700 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1701 swap_direction = true;
1704 framecnt_t frame_delta = 0;
1706 bool left_direction = false;
1707 if (last_pointer_frame() > adjusted_current_frame(event)) {
1708 left_direction = true;
1711 if (left_direction) {
1712 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1714 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1717 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1718 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1724 switch (_operation) {
1726 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1729 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1732 show_verbose_cursor_time (adjusted_current_frame (event));
1739 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1741 if (movement_occurred) {
1742 motion (event, false);
1744 /* This must happen before the region's StatefulDiffCommand is created, as it may
1745 `correct' (ahem) the region's _start from being negative to being zero. It
1746 needs to be zero in the undo record.
1748 if (_operation == StartTrim) {
1749 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1750 i->view->trim_front_ending ();
1754 if (!_editor->selection->selected (_primary)) {
1755 _primary->thaw_after_trim ();
1758 set<boost::shared_ptr<Playlist> > diffed_playlists;
1760 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1761 i->view->thaw_after_trim ();
1762 i->view->enable_display (true);
1763 i->view->fake_set_opaque (true);
1765 /* Trimming one region may affect others on the playlist, so we need
1766 to get undo Commands from the whole playlist rather than just the
1767 region. Use diffed_playlists to make sure we don't diff a given
1768 playlist more than once.
1770 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1771 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1772 vector<Command*> cmds;
1774 _editor->session()->add_commands (cmds);
1775 diffed_playlists.insert (p);
1779 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1783 _editor->motion_frozen_playlists.clear ();
1784 _editor->commit_reversible_command();
1787 /* no mouse movement */
1788 _editor->point_trim (event, adjusted_current_frame (event));
1791 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1792 if (_operation == StartTrim) {
1793 i->view->trim_front_ending ();
1796 i->view->region()->resume_property_changes ();
1801 TrimDrag::aborted (bool movement_occurred)
1803 /* Our motion method is changing model state, so use the Undo system
1804 to cancel. Perhaps not ideal, as this will leave an Undo point
1805 behind which may be slightly odd from the user's point of view.
1810 if (movement_occurred) {
1814 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1815 i->view->region()->resume_property_changes ();
1820 TrimDrag::setup_pointer_frame_offset ()
1822 list<DraggingView>::iterator i = _views.begin ();
1823 while (i != _views.end() && i->view != _primary) {
1827 if (i == _views.end()) {
1831 switch (_operation) {
1833 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1836 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1843 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1847 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1848 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1853 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1855 Drag::start_grab (event, cursor);
1856 show_verbose_cursor_time (adjusted_current_frame(event));
1860 MeterMarkerDrag::setup_pointer_frame_offset ()
1862 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1866 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1870 // create a dummy marker for visual representation of moving the
1871 // section, because whether its a copy or not, we're going to
1872 // leave or lose the original marker (leave if its a copy; lose if its
1873 // not, because we'll remove it from the map).
1875 MeterSection section (_marker->meter());
1877 if (!section.movable()) {
1882 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1884 _marker = new MeterMarker (
1886 *_editor->meter_group,
1887 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1889 *new MeterSection (_marker->meter())
1892 /* use the new marker for the grab */
1893 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1896 TempoMap& map (_editor->session()->tempo_map());
1897 /* remove the section while we drag it */
1898 map.remove_meter (section);
1902 framepos_t const pf = adjusted_current_frame (event);
1903 _marker->set_position (pf);
1904 show_verbose_cursor_time (pf);
1908 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1910 if (!movement_occurred) {
1914 motion (event, false);
1916 Timecode::BBT_Time when;
1918 TempoMap& map (_editor->session()->tempo_map());
1919 map.bbt_time (last_pointer_frame(), when);
1921 if (_copy == true) {
1922 _editor->begin_reversible_command (_("copy meter mark"));
1923 XMLNode &before = map.get_state();
1924 map.add_meter (_marker->meter(), when);
1925 XMLNode &after = map.get_state();
1926 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1927 _editor->commit_reversible_command ();
1930 _editor->begin_reversible_command (_("move meter mark"));
1931 XMLNode &before = map.get_state();
1933 /* we removed it before, so add it back now */
1935 map.add_meter (_marker->meter(), when);
1936 XMLNode &after = map.get_state();
1937 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1938 _editor->commit_reversible_command ();
1941 // delete the dummy marker we used for visual representation while moving.
1942 // a new visual marker will show up automatically.
1947 MeterMarkerDrag::aborted (bool)
1949 _marker->set_position (_marker->meter().frame ());
1952 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1956 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1958 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1963 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1967 // create a dummy marker for visual representation of moving the copy.
1968 // The actual copying is not done before we reach the finish callback.
1970 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1972 TempoMarker* new_marker = new TempoMarker (
1974 *_editor->tempo_group,
1975 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1977 *new TempoSection (_marker->tempo())
1980 _item = &new_marker->the_item ();
1981 _marker = new_marker;
1985 Drag::start_grab (event, cursor);
1987 show_verbose_cursor_time (adjusted_current_frame (event));
1991 TempoMarkerDrag::setup_pointer_frame_offset ()
1993 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1997 TempoMarkerDrag::motion (GdkEvent* event, bool)
1999 framepos_t const pf = adjusted_current_frame (event);
2000 _marker->set_position (pf);
2001 show_verbose_cursor_time (pf);
2005 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2007 if (!movement_occurred) {
2011 motion (event, false);
2013 Timecode::BBT_Time when;
2015 TempoMap& map (_editor->session()->tempo_map());
2016 map.bbt_time (last_pointer_frame(), when);
2018 if (_copy == true) {
2019 _editor->begin_reversible_command (_("copy tempo mark"));
2020 XMLNode &before = map.get_state();
2021 map.add_tempo (_marker->tempo(), when);
2022 XMLNode &after = map.get_state();
2023 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2024 _editor->commit_reversible_command ();
2026 // delete the dummy marker we used for visual representation of copying.
2027 // a new visual marker will show up automatically.
2030 _editor->begin_reversible_command (_("move tempo mark"));
2031 XMLNode &before = map.get_state();
2032 map.move_tempo (_marker->tempo(), when);
2033 XMLNode &after = map.get_state();
2034 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2035 _editor->commit_reversible_command ();
2040 TempoMarkerDrag::aborted (bool)
2042 _marker->set_position (_marker->tempo().frame());
2045 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2049 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2052 /** Do all the things we do when dragging the playhead to make it look as though
2053 * we have located, without actually doing the locate (because that would cause
2054 * the diskstream buffers to be refilled, which is too slow).
2057 CursorDrag::fake_locate (framepos_t t)
2059 _editor->playhead_cursor->set_position (t);
2061 Session* s = _editor->session ();
2062 if (s->timecode_transmission_suspended ()) {
2063 framepos_t const f = _editor->playhead_cursor->current_frame;
2064 s->send_mmc_locate (f);
2065 s->send_full_time_code (f);
2068 show_verbose_cursor_time (t);
2069 _editor->UpdateAllTransportClocks (t);
2073 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2075 Drag::start_grab (event, c);
2077 _grab_zoom = _editor->frames_per_unit;
2079 framepos_t where = _editor->event_frame (event, 0, 0);
2080 _editor->snap_to_with_modifier (where, event);
2082 _editor->_dragging_playhead = true;
2084 Session* s = _editor->session ();
2087 if (_was_rolling && _stop) {
2091 if (s->is_auditioning()) {
2092 s->cancel_audition ();
2095 s->request_suspend_timecode_transmission ();
2096 while (!s->timecode_transmission_suspended ()) {
2097 /* twiddle our thumbs */
2101 fake_locate (where);
2105 CursorDrag::motion (GdkEvent* event, bool)
2107 framepos_t const adjusted_frame = adjusted_current_frame (event);
2108 if (adjusted_frame != last_pointer_frame()) {
2109 fake_locate (adjusted_frame);
2111 _editor->update_canvas_now ();
2117 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2119 _editor->_dragging_playhead = false;
2121 if (!movement_occurred && _stop) {
2125 motion (event, false);
2127 Session* s = _editor->session ();
2129 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2130 _editor->_pending_locate_request = true;
2131 s->request_resume_timecode_transmission ();
2136 CursorDrag::aborted (bool)
2138 if (_editor->_dragging_playhead) {
2139 _editor->session()->request_resume_timecode_transmission ();
2140 _editor->_dragging_playhead = false;
2143 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2146 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2147 : RegionDrag (e, i, p, v)
2149 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2153 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2155 Drag::start_grab (event, cursor);
2157 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2158 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2160 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2162 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2166 FadeInDrag::setup_pointer_frame_offset ()
2168 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2169 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2170 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2174 FadeInDrag::motion (GdkEvent* event, bool)
2176 framecnt_t fade_length;
2178 framepos_t const pos = adjusted_current_frame (event);
2180 boost::shared_ptr<Region> region = _primary->region ();
2182 if (pos < (region->position() + 64)) {
2183 fade_length = 64; // this should be a minimum defined somewhere
2184 } else if (pos > region->last_frame()) {
2185 fade_length = region->length();
2187 fade_length = pos - region->position();
2190 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2192 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2198 tmp->reset_fade_in_shape_width (fade_length);
2199 tmp->show_fade_line((framecnt_t) fade_length);
2202 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2206 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2208 if (!movement_occurred) {
2212 framecnt_t fade_length;
2214 framepos_t const pos = adjusted_current_frame (event);
2216 boost::shared_ptr<Region> region = _primary->region ();
2218 if (pos < (region->position() + 64)) {
2219 fade_length = 64; // this should be a minimum defined somewhere
2220 } else if (pos > region->last_frame()) {
2221 fade_length = region->length();
2223 fade_length = pos - region->position();
2226 _editor->begin_reversible_command (_("change fade in length"));
2228 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2230 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2236 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2237 XMLNode &before = alist->get_state();
2239 tmp->audio_region()->set_fade_in_length (fade_length);
2240 tmp->audio_region()->set_fade_in_active (true);
2241 tmp->hide_fade_line();
2243 XMLNode &after = alist->get_state();
2244 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2247 _editor->commit_reversible_command ();
2251 FadeInDrag::aborted (bool)
2253 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2254 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2260 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2261 tmp->hide_fade_line();
2265 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2266 : RegionDrag (e, i, p, v)
2268 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2272 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2274 Drag::start_grab (event, cursor);
2276 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2277 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2279 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2281 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2285 FadeOutDrag::setup_pointer_frame_offset ()
2287 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2288 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2289 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2293 FadeOutDrag::motion (GdkEvent* event, bool)
2295 framecnt_t fade_length;
2297 framepos_t const pos = adjusted_current_frame (event);
2299 boost::shared_ptr<Region> region = _primary->region ();
2301 if (pos > (region->last_frame() - 64)) {
2302 fade_length = 64; // this should really be a minimum fade defined somewhere
2304 else if (pos < region->position()) {
2305 fade_length = region->length();
2308 fade_length = region->last_frame() - pos;
2311 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2313 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2319 tmp->reset_fade_out_shape_width (fade_length);
2320 tmp->show_fade_line(region->length() - fade_length);
2323 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2327 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2329 if (!movement_occurred) {
2333 framecnt_t fade_length;
2335 framepos_t const pos = adjusted_current_frame (event);
2337 boost::shared_ptr<Region> region = _primary->region ();
2339 if (pos > (region->last_frame() - 64)) {
2340 fade_length = 64; // this should really be a minimum fade defined somewhere
2342 else if (pos < region->position()) {
2343 fade_length = region->length();
2346 fade_length = region->last_frame() - pos;
2349 _editor->begin_reversible_command (_("change fade out length"));
2351 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2353 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2359 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2360 XMLNode &before = alist->get_state();
2362 tmp->audio_region()->set_fade_out_length (fade_length);
2363 tmp->audio_region()->set_fade_out_active (true);
2364 tmp->hide_fade_line();
2366 XMLNode &after = alist->get_state();
2367 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2370 _editor->commit_reversible_command ();
2374 FadeOutDrag::aborted (bool)
2376 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2377 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2383 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2384 tmp->hide_fade_line();
2388 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2391 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2393 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2396 _points.push_back (Gnome::Art::Point (0, 0));
2397 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2400 MarkerDrag::~MarkerDrag ()
2402 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2408 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2410 Drag::start_grab (event, cursor);
2414 Location *location = _editor->find_location_from_marker (_marker, is_start);
2415 _editor->_dragging_edit_point = true;
2417 update_item (location);
2419 // _drag_line->show();
2420 // _line->raise_to_top();
2423 show_verbose_cursor_time (location->start());
2425 show_verbose_cursor_time (location->end());
2428 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2431 case Selection::Toggle:
2432 _editor->selection->toggle (_marker);
2434 case Selection::Set:
2435 if (!_editor->selection->selected (_marker)) {
2436 _editor->selection->set (_marker);
2439 case Selection::Extend:
2441 Locations::LocationList ll;
2442 list<Marker*> to_add;
2444 _editor->selection->markers.range (s, e);
2445 s = min (_marker->position(), s);
2446 e = max (_marker->position(), e);
2449 if (e < max_framepos) {
2452 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2453 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2454 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2457 to_add.push_back (lm->start);
2460 to_add.push_back (lm->end);
2464 if (!to_add.empty()) {
2465 _editor->selection->add (to_add);
2469 case Selection::Add:
2470 _editor->selection->add (_marker);
2474 /* Set up copies for us to manipulate during the drag */
2476 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2477 Location* l = _editor->find_location_from_marker (*i, is_start);
2478 _copied_locations.push_back (new Location (*l));
2483 MarkerDrag::setup_pointer_frame_offset ()
2486 Location *location = _editor->find_location_from_marker (_marker, is_start);
2487 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2491 MarkerDrag::motion (GdkEvent* event, bool)
2493 framecnt_t f_delta = 0;
2495 bool move_both = false;
2497 Location *real_location;
2498 Location *copy_location = 0;
2500 framepos_t const newframe = adjusted_current_frame (event);
2502 framepos_t next = newframe;
2504 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2508 MarkerSelection::iterator i;
2509 list<Location*>::iterator x;
2511 /* find the marker we're dragging, and compute the delta */
2513 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2514 x != _copied_locations.end() && i != _editor->selection->markers.end();
2520 if (marker == _marker) {
2522 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2527 if (real_location->is_mark()) {
2528 f_delta = newframe - copy_location->start();
2532 switch (marker->type()) {
2533 case Marker::SessionStart:
2534 case Marker::RangeStart:
2535 case Marker::LoopStart:
2536 case Marker::PunchIn:
2537 f_delta = newframe - copy_location->start();
2540 case Marker::SessionEnd:
2541 case Marker::RangeEnd:
2542 case Marker::LoopEnd:
2543 case Marker::PunchOut:
2544 f_delta = newframe - copy_location->end();
2547 /* what kind of marker is this ? */
2555 if (i == _editor->selection->markers.end()) {
2556 /* hmm, impossible - we didn't find the dragged marker */
2560 /* now move them all */
2562 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2563 x != _copied_locations.end() && i != _editor->selection->markers.end();
2569 /* call this to find out if its the start or end */
2571 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2575 if (real_location->locked()) {
2579 if (copy_location->is_mark()) {
2583 copy_location->set_start (copy_location->start() + f_delta);
2587 framepos_t new_start = copy_location->start() + f_delta;
2588 framepos_t new_end = copy_location->end() + f_delta;
2590 if (is_start) { // start-of-range marker
2593 copy_location->set_start (new_start);
2594 copy_location->set_end (new_end);
2595 } else if (new_start < copy_location->end()) {
2596 copy_location->set_start (new_start);
2597 } else if (newframe > 0) {
2598 _editor->snap_to (next, 1, true);
2599 copy_location->set_end (next);
2600 copy_location->set_start (newframe);
2603 } else { // end marker
2606 copy_location->set_end (new_end);
2607 copy_location->set_start (new_start);
2608 } else if (new_end > copy_location->start()) {
2609 copy_location->set_end (new_end);
2610 } else if (newframe > 0) {
2611 _editor->snap_to (next, -1, true);
2612 copy_location->set_start (next);
2613 copy_location->set_end (newframe);
2618 update_item (copy_location);
2620 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2623 lm->set_position (copy_location->start(), copy_location->end());
2627 assert (!_copied_locations.empty());
2629 show_verbose_cursor_time (newframe);
2632 _editor->update_canvas_now ();
2637 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2639 if (!movement_occurred) {
2641 /* just a click, do nothing but finish
2642 off the selection process
2645 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2648 case Selection::Set:
2649 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2650 _editor->selection->set (_marker);
2654 case Selection::Toggle:
2655 case Selection::Extend:
2656 case Selection::Add:
2663 _editor->_dragging_edit_point = false;
2665 _editor->begin_reversible_command ( _("move marker") );
2666 XMLNode &before = _editor->session()->locations()->get_state();
2668 MarkerSelection::iterator i;
2669 list<Location*>::iterator x;
2672 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2673 x != _copied_locations.end() && i != _editor->selection->markers.end();
2676 Location * location = _editor->find_location_from_marker (*i, is_start);
2680 if (location->locked()) {
2684 if (location->is_mark()) {
2685 location->set_start ((*x)->start());
2687 location->set ((*x)->start(), (*x)->end());
2692 XMLNode &after = _editor->session()->locations()->get_state();
2693 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2694 _editor->commit_reversible_command ();
2698 MarkerDrag::aborted (bool)
2704 MarkerDrag::update_item (Location*)
2709 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2711 _cumulative_x_drag (0),
2712 _cumulative_y_drag (0)
2714 if (_zero_gain_fraction < 0.0) {
2715 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2718 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2720 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2726 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2728 Drag::start_grab (event, _editor->cursors()->fader);
2730 // start the grab at the center of the control point so
2731 // the point doesn't 'jump' to the mouse after the first drag
2732 _fixed_grab_x = _point->get_x();
2733 _fixed_grab_y = _point->get_y();
2735 float const fraction = 1 - (_point->get_y() / _point->line().height());
2737 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2739 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2740 event->button.x + 10, event->button.y + 10);
2742 _editor->verbose_cursor()->show ();
2746 ControlPointDrag::motion (GdkEvent* event, bool)
2748 double dx = _drags->current_pointer_x() - last_pointer_x();
2749 double dy = _drags->current_pointer_y() - last_pointer_y();
2751 if (event->button.state & Keyboard::SecondaryModifier) {
2756 /* coordinate in pixels relative to the start of the region (for region-based automation)
2757 or track (for track-based automation) */
2758 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2759 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2761 // calculate zero crossing point. back off by .01 to stay on the
2762 // positive side of zero
2763 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2765 // make sure we hit zero when passing through
2766 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2770 if (_x_constrained) {
2773 if (_y_constrained) {
2777 _cumulative_x_drag = cx - _fixed_grab_x;
2778 _cumulative_y_drag = cy - _fixed_grab_y;
2782 cy = min ((double) _point->line().height(), cy);
2784 framepos_t cx_frames = _editor->unit_to_frame (cx);
2786 if (!_x_constrained) {
2787 _editor->snap_to_with_modifier (cx_frames, event);
2790 cx_frames = min (cx_frames, _point->line().maximum_time());
2792 float const fraction = 1.0 - (cy / _point->line().height());
2794 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2796 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2798 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2802 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2804 if (!movement_occurred) {
2808 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2809 _editor->reset_point_selection ();
2813 motion (event, false);
2816 _point->line().end_drag ();
2817 _editor->session()->commit_reversible_command ();
2821 ControlPointDrag::aborted (bool)
2823 _point->line().reset ();
2827 ControlPointDrag::active (Editing::MouseMode m)
2829 if (m == Editing::MouseGain) {
2830 /* always active in mouse gain */
2834 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2835 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2838 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2841 _cumulative_y_drag (0)
2843 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2847 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2849 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2852 _item = &_line->grab_item ();
2854 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2855 origin, and ditto for y.
2858 double cx = event->button.x;
2859 double cy = event->button.y;
2861 _line->parent_group().w2i (cx, cy);
2863 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2868 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2869 /* no adjacent points */
2873 Drag::start_grab (event, _editor->cursors()->fader);
2875 /* store grab start in parent frame */
2880 double fraction = 1.0 - (cy / _line->height());
2882 _line->start_drag_line (before, after, fraction);
2884 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2885 event->button.x + 10, event->button.y + 10);
2887 _editor->verbose_cursor()->show ();
2891 LineDrag::motion (GdkEvent* event, bool)
2893 double dy = _drags->current_pointer_y() - last_pointer_y();
2895 if (event->button.state & Keyboard::SecondaryModifier) {
2899 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2901 _cumulative_y_drag = cy - _fixed_grab_y;
2904 cy = min ((double) _line->height(), cy);
2906 double const fraction = 1.0 - (cy / _line->height());
2910 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2916 /* we are ignoring x position for this drag, so we can just pass in anything */
2917 _line->drag_motion (0, fraction, true, push);
2919 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2923 LineDrag::finished (GdkEvent* event, bool)
2925 motion (event, false);
2927 _editor->session()->commit_reversible_command ();
2931 LineDrag::aborted (bool)
2936 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2939 _cumulative_x_drag (0)
2941 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2945 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2947 Drag::start_grab (event);
2949 _line = reinterpret_cast<Line*> (_item);
2952 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2954 double cx = event->button.x;
2955 double cy = event->button.y;
2957 _item->property_parent().get_value()->w2i(cx, cy);
2959 /* store grab start in parent frame */
2960 _region_view_grab_x = cx;
2962 _before = *(float*) _item->get_data ("position");
2964 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2966 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2970 FeatureLineDrag::motion (GdkEvent*, bool)
2972 double dx = _drags->current_pointer_x() - last_pointer_x();
2974 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2976 _cumulative_x_drag += dx;
2978 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2987 ArdourCanvas::Points points;
2989 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
2991 _line->get_bounds(x1, y2, x2, y2);
2993 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
2994 points.push_back(Gnome::Art::Point(cx, y2 - y1));
2996 _line->property_points() = points;
2998 float *pos = new float;
3001 _line->set_data ("position", pos);
3007 FeatureLineDrag::finished (GdkEvent*, bool)
3009 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3010 _arv->update_transient(_before, _before);
3014 FeatureLineDrag::aborted (bool)
3019 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3022 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3026 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3028 Drag::start_grab (event);
3029 show_verbose_cursor_time (adjusted_current_frame (event));
3033 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3040 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3042 framepos_t grab = grab_frame ();
3043 if (Config->get_rubberbanding_snaps_to_grid ()) {
3044 _editor->snap_to_with_modifier (grab, event);
3047 /* base start and end on initial click position */
3057 if (_drags->current_pointer_y() < grab_y()) {
3058 y1 = _drags->current_pointer_y();
3061 y2 = _drags->current_pointer_y();
3066 if (start != end || y1 != y2) {
3068 double x1 = _editor->frame_to_pixel (start);
3069 double x2 = _editor->frame_to_pixel (end);
3071 _editor->rubberband_rect->property_x1() = x1;
3072 _editor->rubberband_rect->property_y1() = y1;
3073 _editor->rubberband_rect->property_x2() = x2;
3074 _editor->rubberband_rect->property_y2() = y2;
3076 _editor->rubberband_rect->show();
3077 _editor->rubberband_rect->raise_to_top();
3079 show_verbose_cursor_time (pf);
3081 do_select_things (event, true);
3086 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3091 if (grab_frame() < last_pointer_frame()) {
3093 x2 = last_pointer_frame ();
3096 x1 = last_pointer_frame ();
3102 if (_drags->current_pointer_y() < grab_y()) {
3103 y1 = _drags->current_pointer_y();
3106 y2 = _drags->current_pointer_y();
3110 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3114 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3116 if (movement_occurred) {
3118 motion (event, false);
3119 do_select_things (event, false);
3127 _editor->rubberband_rect->hide();
3131 RubberbandSelectDrag::aborted (bool)
3133 _editor->rubberband_rect->hide ();
3136 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3137 : RegionDrag (e, i, p, v)
3139 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3143 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3145 Drag::start_grab (event, cursor);
3147 show_verbose_cursor_time (adjusted_current_frame (event));
3151 TimeFXDrag::motion (GdkEvent* event, bool)
3153 RegionView* rv = _primary;
3155 framepos_t const pf = adjusted_current_frame (event);
3157 if (pf > rv->region()->position()) {
3158 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3161 show_verbose_cursor_time (pf);
3165 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3167 _primary->get_time_axis_view().hide_timestretch ();
3169 if (!movement_occurred) {
3173 if (last_pointer_frame() < _primary->region()->position()) {
3174 /* backwards drag of the left edge - not usable */
3178 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3180 float percentage = (double) newlen / (double) _primary->region()->length();
3182 #ifndef USE_RUBBERBAND
3183 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3184 if (_primary->region()->data_type() == DataType::AUDIO) {
3185 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3189 // XXX how do timeFX on multiple regions ?
3194 if (_editor->time_stretch (rs, percentage) == -1) {
3195 error << _("An error occurred while executing time stretch operation") << endmsg;
3200 TimeFXDrag::aborted (bool)
3202 _primary->get_time_axis_view().hide_timestretch ();
3205 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3208 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3212 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3214 Drag::start_grab (event);
3218 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3220 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3224 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3226 if (movement_occurred && _editor->session()) {
3227 /* make sure we stop */
3228 _editor->session()->request_transport_speed (0.0);
3233 ScrubDrag::aborted (bool)
3238 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3242 , _original_pointer_time_axis (-1)
3243 , _last_pointer_time_axis (-1)
3245 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3249 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3251 if (_editor->session() == 0) {
3255 Gdk::Cursor* cursor = 0;
3257 switch (_operation) {
3258 case CreateSelection:
3259 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3264 cursor = _editor->cursors()->selector;
3265 Drag::start_grab (event, cursor);
3268 case SelectionStartTrim:
3269 if (_editor->clicked_axisview) {
3270 _editor->clicked_axisview->order_selection_trims (_item, true);
3272 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3275 case SelectionEndTrim:
3276 if (_editor->clicked_axisview) {
3277 _editor->clicked_axisview->order_selection_trims (_item, false);
3279 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3283 Drag::start_grab (event, cursor);
3287 if (_operation == SelectionMove) {
3288 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3290 show_verbose_cursor_time (adjusted_current_frame (event));
3293 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3297 SelectionDrag::setup_pointer_frame_offset ()
3299 switch (_operation) {
3300 case CreateSelection:
3301 _pointer_frame_offset = 0;
3304 case SelectionStartTrim:
3306 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3309 case SelectionEndTrim:
3310 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3316 SelectionDrag::motion (GdkEvent* event, bool first_move)
3318 framepos_t start = 0;
3322 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3323 if (pending_time_axis.first == 0) {
3327 framepos_t const pending_position = adjusted_current_frame (event);
3329 /* only alter selection if things have changed */
3331 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3335 switch (_operation) {
3336 case CreateSelection:
3338 framepos_t grab = grab_frame ();
3341 _editor->snap_to (grab);
3344 if (pending_position < grab_frame()) {
3345 start = pending_position;
3348 end = pending_position;
3352 /* first drag: Either add to the selection
3353 or create a new selection
3359 /* adding to the selection */
3360 _editor->set_selected_track_as_side_effect (Selection::Add);
3361 //_editor->selection->add (_editor->clicked_axisview);
3362 _editor->clicked_selection = _editor->selection->add (start, end);
3367 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3368 //_editor->selection->set (_editor->clicked_axisview);
3369 _editor->set_selected_track_as_side_effect (Selection::Set);
3372 _editor->clicked_selection = _editor->selection->set (start, end);
3376 /* select the track that we're in */
3377 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3378 // _editor->set_selected_track_as_side_effect (Selection::Add);
3379 _editor->selection->add (pending_time_axis.first);
3380 _added_time_axes.push_back (pending_time_axis.first);
3383 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3384 tracks that we selected in the first place.
3387 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3388 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3390 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3391 while (i != _added_time_axes.end()) {
3393 list<TimeAxisView*>::iterator tmp = i;
3396 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3397 _editor->selection->remove (*i);
3398 _added_time_axes.remove (*i);
3407 case SelectionStartTrim:
3409 start = _editor->selection->time[_editor->clicked_selection].start;
3410 end = _editor->selection->time[_editor->clicked_selection].end;
3412 if (pending_position > end) {
3415 start = pending_position;
3419 case SelectionEndTrim:
3421 start = _editor->selection->time[_editor->clicked_selection].start;
3422 end = _editor->selection->time[_editor->clicked_selection].end;
3424 if (pending_position < start) {
3427 end = pending_position;
3434 start = _editor->selection->time[_editor->clicked_selection].start;
3435 end = _editor->selection->time[_editor->clicked_selection].end;
3437 length = end - start;
3439 start = pending_position;
3440 _editor->snap_to (start);
3442 end = start + length;
3447 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3448 _editor->start_canvas_autoscroll (1, 0);
3452 _editor->selection->replace (_editor->clicked_selection, start, end);
3455 if (_operation == SelectionMove) {
3456 show_verbose_cursor_time(start);
3458 show_verbose_cursor_time(pending_position);
3463 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3465 Session* s = _editor->session();
3467 if (movement_occurred) {
3468 motion (event, false);
3469 /* XXX this is not object-oriented programming at all. ick */
3470 if (_editor->selection->time.consolidate()) {
3471 _editor->selection->TimeChanged ();
3474 /* XXX what if its a music time selection? */
3475 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3476 s->request_play_range (&_editor->selection->time, true);
3481 /* just a click, no pointer movement.*/
3483 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3484 _editor->selection->clear_time();
3487 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3488 _editor->selection->set (_editor->clicked_axisview);
3491 if (s && s->get_play_range () && s->transport_rolling()) {
3492 s->request_stop (false, false);
3497 _editor->stop_canvas_autoscroll ();
3501 SelectionDrag::aborted (bool)
3506 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3511 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3513 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3514 physical_screen_height (_editor->get_window()));
3515 _drag_rect->hide ();
3517 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3518 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3522 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3524 if (_editor->session() == 0) {
3528 Gdk::Cursor* cursor = 0;
3530 if (!_editor->temp_location) {
3531 _editor->temp_location = new Location (*_editor->session());
3534 switch (_operation) {
3535 case CreateRangeMarker:
3536 case CreateTransportMarker:
3537 case CreateCDMarker:
3539 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3544 cursor = _editor->cursors()->selector;
3548 Drag::start_grab (event, cursor);
3550 show_verbose_cursor_time (adjusted_current_frame (event));
3554 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3556 framepos_t start = 0;
3558 ArdourCanvas::SimpleRect *crect;
3560 switch (_operation) {
3561 case CreateRangeMarker:
3562 crect = _editor->range_bar_drag_rect;
3564 case CreateTransportMarker:
3565 crect = _editor->transport_bar_drag_rect;
3567 case CreateCDMarker:
3568 crect = _editor->cd_marker_bar_drag_rect;
3571 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3576 framepos_t const pf = adjusted_current_frame (event);
3578 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3579 framepos_t grab = grab_frame ();
3580 _editor->snap_to (grab);
3582 if (pf < grab_frame()) {
3590 /* first drag: Either add to the selection
3591 or create a new selection.
3596 _editor->temp_location->set (start, end);
3600 update_item (_editor->temp_location);
3602 //_drag_rect->raise_to_top();
3607 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3608 _editor->start_canvas_autoscroll (1, 0);
3612 _editor->temp_location->set (start, end);
3614 double x1 = _editor->frame_to_pixel (start);
3615 double x2 = _editor->frame_to_pixel (end);
3616 crect->property_x1() = x1;
3617 crect->property_x2() = x2;
3619 update_item (_editor->temp_location);
3622 show_verbose_cursor_time (pf);
3627 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3629 Location * newloc = 0;
3633 if (movement_occurred) {
3634 motion (event, false);
3637 switch (_operation) {
3638 case CreateRangeMarker:
3639 case CreateCDMarker:
3641 _editor->begin_reversible_command (_("new range marker"));
3642 XMLNode &before = _editor->session()->locations()->get_state();
3643 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3644 if (_operation == CreateCDMarker) {
3645 flags = Location::IsRangeMarker | Location::IsCDMarker;
3646 _editor->cd_marker_bar_drag_rect->hide();
3649 flags = Location::IsRangeMarker;
3650 _editor->range_bar_drag_rect->hide();
3652 newloc = new Location (
3653 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3656 _editor->session()->locations()->add (newloc, true);
3657 XMLNode &after = _editor->session()->locations()->get_state();
3658 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3659 _editor->commit_reversible_command ();
3663 case CreateTransportMarker:
3664 // popup menu to pick loop or punch
3665 _editor->new_transport_marker_context_menu (&event->button, _item);
3669 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3671 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3676 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3678 if (end == max_framepos) {
3679 end = _editor->session()->current_end_frame ();
3682 if (start == max_framepos) {
3683 start = _editor->session()->current_start_frame ();
3686 switch (_editor->mouse_mode) {
3688 /* find the two markers on either side and then make the selection from it */
3689 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3693 /* find the two markers on either side of the click and make the range out of it */
3694 _editor->selection->set (start, end);
3703 _editor->stop_canvas_autoscroll ();
3707 RangeMarkerBarDrag::aborted (bool)
3713 RangeMarkerBarDrag::update_item (Location* location)
3715 double const x1 = _editor->frame_to_pixel (location->start());
3716 double const x2 = _editor->frame_to_pixel (location->end());
3718 _drag_rect->property_x1() = x1;
3719 _drag_rect->property_x2() = x2;
3722 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3726 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3730 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3732 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3733 Drag::start_grab (event, _editor->cursors()->zoom_out);
3736 Drag::start_grab (event, _editor->cursors()->zoom_in);
3740 show_verbose_cursor_time (adjusted_current_frame (event));
3744 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3749 framepos_t const pf = adjusted_current_frame (event);
3751 framepos_t grab = grab_frame ();
3752 _editor->snap_to_with_modifier (grab, event);
3754 /* base start and end on initial click position */
3766 _editor->zoom_rect->show();
3767 _editor->zoom_rect->raise_to_top();
3770 _editor->reposition_zoom_rect(start, end);
3772 show_verbose_cursor_time (pf);
3777 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3779 if (movement_occurred) {
3780 motion (event, false);
3782 if (grab_frame() < last_pointer_frame()) {
3783 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3785 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3788 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3789 _editor->tav_zoom_step (_zoom_out);
3791 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3795 _editor->zoom_rect->hide();
3799 MouseZoomDrag::aborted (bool)
3801 _editor->zoom_rect->hide ();
3804 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3806 , _cumulative_dx (0)
3807 , _cumulative_dy (0)
3809 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3811 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3812 _region = &_primary->region_view ();
3813 _note_height = _region->midi_stream_view()->note_height ();
3817 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3819 Drag::start_grab (event);
3821 if (!(_was_selected = _primary->selected())) {
3823 /* tertiary-click means extend selection - we'll do that on button release,
3824 so don't add it here, because otherwise we make it hard to figure
3825 out the "extend-to" range.
3828 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3831 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3834 _region->note_selected (_primary, true);
3836 _region->unique_select (_primary);
3842 /** @return Current total drag x change in frames */
3844 NoteDrag::total_dx () const
3847 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3849 /* primary note time */
3850 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3852 /* new time of the primary note in session frames */
3853 frameoffset_t st = n + dx;
3855 framepos_t const rp = _region->region()->position ();
3857 /* prevent the note being dragged earlier than the region's position */
3860 /* snap and return corresponding delta */
3861 return _region->snap_frame_to_frame (st - rp) + rp - n;
3864 /** @return Current total drag y change in note number */
3866 NoteDrag::total_dy () const
3868 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3872 NoteDrag::motion (GdkEvent *, bool)
3874 /* Total change in x and y since the start of the drag */
3875 frameoffset_t const dx = total_dx ();
3876 int8_t const dy = total_dy ();
3878 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3879 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3880 double const tdy = -dy * _note_height - _cumulative_dy;
3883 _cumulative_dx += tdx;
3884 _cumulative_dy += tdy;
3886 int8_t note_delta = total_dy();
3888 _region->move_selection (tdx, tdy, note_delta);
3890 /* the new note value may be the same as the old one, but we
3891 * don't know what that means because the selection may have
3892 * involved more than one note and we might be doing something
3893 * odd with them. so show the note value anyway, always.
3897 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3899 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3900 (int) floor (new_note));
3902 show_verbose_cursor_text (buf);
3907 NoteDrag::finished (GdkEvent* ev, bool moved)
3910 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3912 if (_was_selected) {
3913 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3915 _region->note_deselected (_primary);
3918 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3919 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3921 if (!extend && !add && _region->selection_size() > 1) {
3922 _region->unique_select (_primary);
3923 } else if (extend) {
3924 _region->note_selected (_primary, true, true);
3926 /* it was added during button press */
3931 _region->note_dropped (_primary, total_dx(), total_dy());
3936 NoteDrag::aborted (bool)
3941 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3942 : Drag (editor, item)
3944 , _nothing_to_drag (false)
3946 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3948 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3951 /* get all lines in the automation view */
3952 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3954 /* find those that overlap the ranges being dragged */
3955 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3956 while (i != lines.end ()) {
3957 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3960 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3962 /* check this range against all the AudioRanges that we are using */
3963 list<AudioRange>::const_iterator k = _ranges.begin ();
3964 while (k != _ranges.end()) {
3965 if (k->coverage (r.first, r.second) != OverlapNone) {
3971 /* add it to our list if it overlaps at all */
3972 if (k != _ranges.end()) {
3977 _lines.push_back (n);
3983 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3987 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3989 Drag::start_grab (event, cursor);
3991 /* Get line states before we start changing things */
3992 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3993 i->state = &i->line->get_state ();
3996 if (_ranges.empty()) {
3998 /* No selected time ranges: drag all points */
3999 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4000 uint32_t const N = i->line->npoints ();
4001 for (uint32_t j = 0; j < N; ++j) {
4002 i->points.push_back (i->line->nth (j));
4008 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4010 framecnt_t const half = (i->start + i->end) / 2;
4012 /* find the line that this audio range starts in */
4013 list<Line>::iterator j = _lines.begin();
4014 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4018 if (j != _lines.end()) {
4019 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4021 /* j is the line that this audio range starts in; fade into it;
4022 64 samples length plucked out of thin air.
4025 framepos_t a = i->start + 64;
4030 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4031 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4033 the_list->add (p, the_list->eval (p));
4034 j->line->add_always_in_view (p);
4035 the_list->add (q, the_list->eval (q));
4036 j->line->add_always_in_view (q);
4039 /* same thing for the end */
4042 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4046 if (j != _lines.end()) {
4047 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4049 /* j is the line that this audio range starts in; fade out of it;
4050 64 samples length plucked out of thin air.
4053 framepos_t b = i->end - 64;
4058 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4059 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4061 the_list->add (p, the_list->eval (p));
4062 j->line->add_always_in_view (p);
4063 the_list->add (q, the_list->eval (q));
4064 j->line->add_always_in_view (q);
4068 _nothing_to_drag = true;
4070 /* Find all the points that should be dragged and put them in the relevant
4071 points lists in the Line structs.
4074 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4076 uint32_t const N = i->line->npoints ();
4077 for (uint32_t j = 0; j < N; ++j) {
4079 /* here's a control point on this line */
4080 ControlPoint* p = i->line->nth (j);
4081 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4083 /* see if it's inside a range */
4084 list<AudioRange>::const_iterator k = _ranges.begin ();
4085 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4089 if (k != _ranges.end()) {
4090 /* dragging this point */
4091 _nothing_to_drag = false;
4092 i->points.push_back (p);
4098 if (_nothing_to_drag) {
4102 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4103 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4108 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4110 if (_nothing_to_drag) {
4114 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4115 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4117 /* we are ignoring x position for this drag, so we can just pass in anything */
4118 i->line->drag_motion (0, f, true, false);
4123 AutomationRangeDrag::finished (GdkEvent* event, bool)
4125 if (_nothing_to_drag) {
4129 motion (event, false);
4130 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4131 i->line->end_drag ();
4132 i->line->clear_always_in_view ();
4135 _editor->session()->commit_reversible_command ();
4139 AutomationRangeDrag::aborted (bool)
4141 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4142 i->line->clear_always_in_view ();
4147 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4150 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4151 layer = v->region()->layer ();
4152 initial_y = v->get_canvas_group()->property_y ();
4153 initial_playlist = v->region()->playlist ();
4154 initial_position = v->region()->position ();
4155 initial_end = v->region()->position () + v->region()->length ();
4158 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4162 , _cumulative_dx (0)
4164 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4168 PatchChangeDrag::motion (GdkEvent* ev, bool)
4170 framepos_t f = adjusted_current_frame (ev);
4171 boost::shared_ptr<Region> r = _region_view->region ();
4172 f = max (f, r->position ());
4173 f = min (f, r->last_frame ());
4175 framecnt_t const dxf = f - grab_frame();
4176 double const dxu = _editor->frame_to_unit (dxf);
4177 _patch_change->move (dxu - _cumulative_dx, 0);
4178 _cumulative_dx = dxu;
4182 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4184 if (!movement_occurred) {
4188 boost::shared_ptr<Region> r (_region_view->region ());
4190 framepos_t f = adjusted_current_frame (ev);
4191 f = max (f, r->position ());
4192 f = min (f, r->last_frame ());
4194 _region_view->move_patch_change (
4196 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4201 PatchChangeDrag::aborted (bool)
4203 _patch_change->move (-_cumulative_dx, 0);
4207 PatchChangeDrag::setup_pointer_frame_offset ()
4209 boost::shared_ptr<Region> region = _region_view->region ();
4210 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4213 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4214 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4221 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4223 framepos_t const p = _region_view->region()->position ();
4224 double const y = _region_view->midi_view()->y_position ();
4226 x1 = max ((framepos_t) 0, x1 - p);
4227 x2 = max ((framepos_t) 0, x2 - p);
4228 y1 = max (0.0, y1 - y);
4229 y2 = max (0.0, y2 - y);
4231 _region_view->update_drag_selection (
4232 _editor->frame_to_pixel (x1),
4233 _editor->frame_to_pixel (x2),
4236 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4241 MidiRubberbandSelectDrag::deselect_things ()
4246 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4247 : RubberbandSelectDrag (e, i)
4253 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4255 if (drag_in_progress) {
4256 /* We just want to select things at the end of the drag, not during it */
4260 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4262 _editor->begin_reversible_command (_("rubberband selection"));
4263 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4264 _editor->commit_reversible_command ();
4268 EditorRubberbandSelectDrag::deselect_things ()
4270 if (!getenv("ARDOUR_SAE")) {
4271 _editor->selection->clear_tracks();
4273 _editor->selection->clear_regions();
4274 _editor->selection->clear_points ();
4275 _editor->selection->clear_lines ();
4278 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4286 NoteCreateDrag::~NoteCreateDrag ()
4292 NoteCreateDrag::grid_frames (framepos_t t) const
4295 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4300 return _region_view->region_beats_to_region_frames (grid_beats);
4304 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4306 Drag::start_grab (event, cursor);
4308 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4310 framepos_t pf = _drags->current_pointer_frame ();
4311 framecnt_t const g = grid_frames (pf);
4313 /* Hack so that we always snap to the note that we are over, instead of snapping
4314 to the next one if we're more than halfway through the one we're over.
4316 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4320 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4322 MidiStreamView* sv = _region_view->midi_stream_view ();
4323 double const x = _editor->frame_to_pixel (_note[0]);
4324 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4326 _drag_rect->property_x1() = x;
4327 _drag_rect->property_y1() = y;
4328 _drag_rect->property_x2() = x;
4329 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4331 _drag_rect->property_outline_what() = 0xff;
4332 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4333 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4337 NoteCreateDrag::motion (GdkEvent* event, bool)
4339 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4340 double const x = _editor->frame_to_pixel (_note[1]);
4341 if (_note[1] > _note[0]) {
4342 _drag_rect->property_x2() = x;
4344 _drag_rect->property_x1() = x;
4349 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4351 if (!had_movement) {
4355 framepos_t const start = min (_note[0], _note[1]);
4356 framecnt_t length = abs (_note[0] - _note[1]);
4358 framecnt_t const g = grid_frames (start);
4359 if (_editor->snap_mode() == SnapNormal && length < g) {
4363 _region_view->create_note_at (start, _drag_rect->property_y1(), _region_view->region_frames_to_region_beats (length), true, false);
4367 NoteCreateDrag::y_to_region (double y) const
4370 _region_view->get_canvas_group()->w2i (x, y);
4375 NoteCreateDrag::aborted (bool)