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)
1535 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1538 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1542 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1548 RegionGainDrag::finished (GdkEvent *, bool)
1554 RegionGainDrag::aborted (bool)
1559 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1560 : RegionDrag (e, i, p, v)
1562 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1566 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1569 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1570 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1572 if (tv && tv->is_track()) {
1573 speed = tv->track()->speed();
1576 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1577 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1578 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1580 framepos_t const pf = adjusted_current_frame (event);
1582 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1583 /* Move the contents of the region around without changing the region bounds */
1584 _operation = ContentsTrim;
1585 Drag::start_grab (event, _editor->cursors()->trimmer);
1587 /* These will get overridden for a point trim.*/
1588 if (pf < (region_start + region_length/2)) {
1589 /* closer to front */
1590 _operation = StartTrim;
1591 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1594 _operation = EndTrim;
1595 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1599 switch (_operation) {
1601 show_verbose_cursor_time (region_start);
1602 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1603 i->view->trim_front_starting ();
1607 show_verbose_cursor_time (region_end);
1610 show_verbose_cursor_time (pf);
1614 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1615 i->view->region()->suspend_property_changes ();
1620 TrimDrag::motion (GdkEvent* event, bool first_move)
1622 RegionView* rv = _primary;
1625 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1626 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1627 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1629 if (tv && tv->is_track()) {
1630 speed = tv->track()->speed();
1633 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1639 switch (_operation) {
1641 trim_type = "Region start trim";
1644 trim_type = "Region end trim";
1647 trim_type = "Region content trim";
1651 _editor->begin_reversible_command (trim_type);
1653 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1654 RegionView* rv = i->view;
1655 rv->fake_set_opaque (false);
1656 rv->enable_display (false);
1657 rv->region()->playlist()->clear_owned_changes ();
1659 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1662 arv->temporarily_hide_envelope ();
1665 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1666 insert_result = _editor->motion_frozen_playlists.insert (pl);
1668 if (insert_result.second) {
1674 bool non_overlap_trim = false;
1676 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1677 non_overlap_trim = true;
1680 switch (_operation) {
1682 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1683 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1688 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1689 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1695 bool swap_direction = false;
1697 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1698 swap_direction = true;
1701 framecnt_t frame_delta = 0;
1703 bool left_direction = false;
1704 if (last_pointer_frame() > adjusted_current_frame(event)) {
1705 left_direction = true;
1708 if (left_direction) {
1709 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1711 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1714 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1715 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1721 switch (_operation) {
1723 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1726 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1729 show_verbose_cursor_time (adjusted_current_frame (event));
1736 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1738 if (movement_occurred) {
1739 motion (event, false);
1741 /* This must happen before the region's StatefulDiffCommand is created, as it may
1742 `correct' (ahem) the region's _start from being negative to being zero. It
1743 needs to be zero in the undo record.
1745 if (_operation == StartTrim) {
1746 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1747 i->view->trim_front_ending ();
1751 if (!_editor->selection->selected (_primary)) {
1752 _primary->thaw_after_trim ();
1755 set<boost::shared_ptr<Playlist> > diffed_playlists;
1757 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1758 i->view->thaw_after_trim ();
1759 i->view->enable_display (true);
1760 i->view->fake_set_opaque (true);
1762 /* Trimming one region may affect others on the playlist, so we need
1763 to get undo Commands from the whole playlist rather than just the
1764 region. Use diffed_playlists to make sure we don't diff a given
1765 playlist more than once.
1767 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1768 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1769 vector<Command*> cmds;
1771 _editor->session()->add_commands (cmds);
1772 diffed_playlists.insert (p);
1776 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1780 _editor->motion_frozen_playlists.clear ();
1781 _editor->commit_reversible_command();
1784 /* no mouse movement */
1785 _editor->point_trim (event, adjusted_current_frame (event));
1788 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1789 if (_operation == StartTrim) {
1790 i->view->trim_front_ending ();
1793 i->view->region()->resume_property_changes ();
1798 TrimDrag::aborted (bool movement_occurred)
1800 /* Our motion method is changing model state, so use the Undo system
1801 to cancel. Perhaps not ideal, as this will leave an Undo point
1802 behind which may be slightly odd from the user's point of view.
1807 if (movement_occurred) {
1811 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1812 i->view->region()->resume_property_changes ();
1817 TrimDrag::setup_pointer_frame_offset ()
1819 list<DraggingView>::iterator i = _views.begin ();
1820 while (i != _views.end() && i->view != _primary) {
1824 if (i == _views.end()) {
1828 switch (_operation) {
1830 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1833 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1840 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1844 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1846 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1851 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1854 // create a dummy marker for visual representation of moving the copy.
1855 // The actual copying is not done before we reach the finish callback.
1857 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1859 MeterMarker* new_marker = new MeterMarker (
1861 *_editor->meter_group,
1862 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1864 *new MeterSection (_marker->meter())
1867 _item = &new_marker->the_item ();
1868 _marker = new_marker;
1872 MetricSection& section (_marker->meter());
1874 if (!section.movable()) {
1880 Drag::start_grab (event, cursor);
1882 show_verbose_cursor_time (adjusted_current_frame(event));
1886 MeterMarkerDrag::setup_pointer_frame_offset ()
1888 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1892 MeterMarkerDrag::motion (GdkEvent* event, bool)
1894 framepos_t const pf = adjusted_current_frame (event);
1896 _marker->set_position (pf);
1898 show_verbose_cursor_time (pf);
1902 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1904 if (!movement_occurred) {
1908 motion (event, false);
1910 Timecode::BBT_Time when;
1912 TempoMap& map (_editor->session()->tempo_map());
1913 map.bbt_time (last_pointer_frame(), when);
1915 if (_copy == true) {
1916 _editor->begin_reversible_command (_("copy meter mark"));
1917 XMLNode &before = map.get_state();
1918 map.add_meter (_marker->meter(), when);
1919 XMLNode &after = map.get_state();
1920 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1921 _editor->commit_reversible_command ();
1923 // delete the dummy marker we used for visual representation of copying.
1924 // a new visual marker will show up automatically.
1927 _editor->begin_reversible_command (_("move meter mark"));
1928 XMLNode &before = map.get_state();
1929 map.move_meter (_marker->meter(), when);
1930 XMLNode &after = map.get_state();
1931 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1932 _editor->commit_reversible_command ();
1937 MeterMarkerDrag::aborted (bool)
1939 _marker->set_position (_marker->meter().frame ());
1942 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1946 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1948 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1953 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1957 // create a dummy marker for visual representation of moving the copy.
1958 // The actual copying is not done before we reach the finish callback.
1960 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1962 TempoMarker* new_marker = new TempoMarker (
1964 *_editor->tempo_group,
1965 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1967 *new TempoSection (_marker->tempo())
1970 _item = &new_marker->the_item ();
1971 _marker = new_marker;
1975 Drag::start_grab (event, cursor);
1977 show_verbose_cursor_time (adjusted_current_frame (event));
1981 TempoMarkerDrag::setup_pointer_frame_offset ()
1983 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1987 TempoMarkerDrag::motion (GdkEvent* event, bool)
1989 framepos_t const pf = adjusted_current_frame (event);
1990 _marker->set_position (pf);
1991 show_verbose_cursor_time (pf);
1995 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1997 if (!movement_occurred) {
2001 motion (event, false);
2003 Timecode::BBT_Time when;
2005 TempoMap& map (_editor->session()->tempo_map());
2006 map.bbt_time (last_pointer_frame(), when);
2008 if (_copy == true) {
2009 _editor->begin_reversible_command (_("copy tempo mark"));
2010 XMLNode &before = map.get_state();
2011 map.add_tempo (_marker->tempo(), when);
2012 XMLNode &after = map.get_state();
2013 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2014 _editor->commit_reversible_command ();
2016 // delete the dummy marker we used for visual representation of copying.
2017 // a new visual marker will show up automatically.
2020 _editor->begin_reversible_command (_("move tempo mark"));
2021 XMLNode &before = map.get_state();
2022 map.move_tempo (_marker->tempo(), when);
2023 XMLNode &after = map.get_state();
2024 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2025 _editor->commit_reversible_command ();
2030 TempoMarkerDrag::aborted (bool)
2032 _marker->set_position (_marker->tempo().frame());
2035 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2039 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2042 /** Do all the things we do when dragging the playhead to make it look as though
2043 * we have located, without actually doing the locate (because that would cause
2044 * the diskstream buffers to be refilled, which is too slow).
2047 CursorDrag::fake_locate (framepos_t t)
2049 _editor->playhead_cursor->set_position (t);
2051 Session* s = _editor->session ();
2052 if (s->timecode_transmission_suspended ()) {
2053 framepos_t const f = _editor->playhead_cursor->current_frame;
2054 s->send_mmc_locate (f);
2055 s->send_full_time_code (f);
2058 show_verbose_cursor_time (t);
2059 _editor->UpdateAllTransportClocks (t);
2063 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2065 Drag::start_grab (event, c);
2067 _grab_zoom = _editor->frames_per_unit;
2069 framepos_t where = _editor->event_frame (event, 0, 0);
2070 _editor->snap_to_with_modifier (where, event);
2072 _editor->_dragging_playhead = true;
2074 Session* s = _editor->session ();
2077 if (_was_rolling && _stop) {
2081 if (s->is_auditioning()) {
2082 s->cancel_audition ();
2085 s->request_suspend_timecode_transmission ();
2086 while (!s->timecode_transmission_suspended ()) {
2087 /* twiddle our thumbs */
2091 fake_locate (where);
2095 CursorDrag::motion (GdkEvent* event, bool)
2097 if (_drags->current_pointer_y() != last_pointer_y()) {
2099 /* zoom when we move the pointer up and down */
2101 /* y range to operate over (pixels) */
2102 double const y_range = 512;
2103 /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */
2104 double const scale_range = 4;
2105 /* dead zone around the grab point in which to do no zooming (pixels) */
2106 double const dead_zone = 100;
2109 double dy = _drags->current_pointer_y() - grab_y();
2111 if (dy < -dead_zone || dy > dead_zone) {
2112 /* we are outside the dead zone; remove it from our calculation */
2119 /* get a number from -1 to 1 as dy ranges from -y_range to y_range */
2120 double udy = max (min (dy / y_range, 1.0), -1.0);
2122 /* and zoom, using playhead focus temporarily */
2123 Editing::ZoomFocus const zf = _editor->get_zoom_focus ();
2124 _editor->set_zoom_focus (Editing::ZoomFocusPlayhead);
2125 _editor->temporal_zoom (_grab_zoom * pow (scale_range, -udy));
2126 _editor->set_zoom_focus (zf);
2130 framepos_t const adjusted_frame = adjusted_current_frame (event);
2131 if (adjusted_frame != last_pointer_frame()) {
2132 fake_locate (adjusted_frame);
2134 _editor->update_canvas_now ();
2140 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2142 _editor->_dragging_playhead = false;
2144 if (!movement_occurred && _stop) {
2148 motion (event, false);
2150 Session* s = _editor->session ();
2152 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2153 _editor->_pending_locate_request = true;
2154 s->request_resume_timecode_transmission ();
2159 CursorDrag::aborted (bool)
2161 if (_editor->_dragging_playhead) {
2162 _editor->session()->request_resume_timecode_transmission ();
2163 _editor->_dragging_playhead = false;
2166 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2169 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2170 : RegionDrag (e, i, p, v)
2172 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2176 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2178 Drag::start_grab (event, cursor);
2180 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2181 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2183 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2185 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2189 FadeInDrag::setup_pointer_frame_offset ()
2191 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2192 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2193 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2197 FadeInDrag::motion (GdkEvent* event, bool)
2199 framecnt_t fade_length;
2201 framepos_t const pos = adjusted_current_frame (event);
2203 boost::shared_ptr<Region> region = _primary->region ();
2205 if (pos < (region->position() + 64)) {
2206 fade_length = 64; // this should be a minimum defined somewhere
2207 } else if (pos > region->last_frame()) {
2208 fade_length = region->length();
2210 fade_length = pos - region->position();
2213 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2215 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2221 tmp->reset_fade_in_shape_width (fade_length);
2222 tmp->show_fade_line((framecnt_t) fade_length);
2225 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2229 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2231 if (!movement_occurred) {
2235 framecnt_t fade_length;
2237 framepos_t const pos = adjusted_current_frame (event);
2239 boost::shared_ptr<Region> region = _primary->region ();
2241 if (pos < (region->position() + 64)) {
2242 fade_length = 64; // this should be a minimum defined somewhere
2243 } else if (pos > region->last_frame()) {
2244 fade_length = region->length();
2246 fade_length = pos - region->position();
2249 _editor->begin_reversible_command (_("change fade in length"));
2251 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2253 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2259 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2260 XMLNode &before = alist->get_state();
2262 tmp->audio_region()->set_fade_in_length (fade_length);
2263 tmp->audio_region()->set_fade_in_active (true);
2264 tmp->hide_fade_line();
2266 XMLNode &after = alist->get_state();
2267 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2270 _editor->commit_reversible_command ();
2274 FadeInDrag::aborted (bool)
2276 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2277 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2283 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2284 tmp->hide_fade_line();
2288 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2289 : RegionDrag (e, i, p, v)
2291 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2295 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2297 Drag::start_grab (event, cursor);
2299 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2300 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2302 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2304 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2308 FadeOutDrag::setup_pointer_frame_offset ()
2310 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2311 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2312 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2316 FadeOutDrag::motion (GdkEvent* event, bool)
2318 framecnt_t fade_length;
2320 framepos_t const pos = adjusted_current_frame (event);
2322 boost::shared_ptr<Region> region = _primary->region ();
2324 if (pos > (region->last_frame() - 64)) {
2325 fade_length = 64; // this should really be a minimum fade defined somewhere
2327 else if (pos < region->position()) {
2328 fade_length = region->length();
2331 fade_length = region->last_frame() - pos;
2334 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2336 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2342 tmp->reset_fade_out_shape_width (fade_length);
2343 tmp->show_fade_line(region->length() - fade_length);
2346 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2350 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2352 if (!movement_occurred) {
2356 framecnt_t fade_length;
2358 framepos_t const pos = adjusted_current_frame (event);
2360 boost::shared_ptr<Region> region = _primary->region ();
2362 if (pos > (region->last_frame() - 64)) {
2363 fade_length = 64; // this should really be a minimum fade defined somewhere
2365 else if (pos < region->position()) {
2366 fade_length = region->length();
2369 fade_length = region->last_frame() - pos;
2372 _editor->begin_reversible_command (_("change fade out length"));
2374 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2376 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2382 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2383 XMLNode &before = alist->get_state();
2385 tmp->audio_region()->set_fade_out_length (fade_length);
2386 tmp->audio_region()->set_fade_out_active (true);
2387 tmp->hide_fade_line();
2389 XMLNode &after = alist->get_state();
2390 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2393 _editor->commit_reversible_command ();
2397 FadeOutDrag::aborted (bool)
2399 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2400 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2406 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2407 tmp->hide_fade_line();
2411 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2414 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2416 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2419 _points.push_back (Gnome::Art::Point (0, 0));
2420 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2423 MarkerDrag::~MarkerDrag ()
2425 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2431 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2433 Drag::start_grab (event, cursor);
2437 Location *location = _editor->find_location_from_marker (_marker, is_start);
2438 _editor->_dragging_edit_point = true;
2440 update_item (location);
2442 // _drag_line->show();
2443 // _line->raise_to_top();
2446 show_verbose_cursor_time (location->start());
2448 show_verbose_cursor_time (location->end());
2451 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2454 case Selection::Toggle:
2455 _editor->selection->toggle (_marker);
2457 case Selection::Set:
2458 if (!_editor->selection->selected (_marker)) {
2459 _editor->selection->set (_marker);
2462 case Selection::Extend:
2464 Locations::LocationList ll;
2465 list<Marker*> to_add;
2467 _editor->selection->markers.range (s, e);
2468 s = min (_marker->position(), s);
2469 e = max (_marker->position(), e);
2472 if (e < max_framepos) {
2475 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2476 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2477 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2480 to_add.push_back (lm->start);
2483 to_add.push_back (lm->end);
2487 if (!to_add.empty()) {
2488 _editor->selection->add (to_add);
2492 case Selection::Add:
2493 _editor->selection->add (_marker);
2497 /* Set up copies for us to manipulate during the drag */
2499 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2500 Location* l = _editor->find_location_from_marker (*i, is_start);
2501 _copied_locations.push_back (new Location (*l));
2506 MarkerDrag::setup_pointer_frame_offset ()
2509 Location *location = _editor->find_location_from_marker (_marker, is_start);
2510 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2514 MarkerDrag::motion (GdkEvent* event, bool)
2516 framecnt_t f_delta = 0;
2518 bool move_both = false;
2520 Location *real_location;
2521 Location *copy_location = 0;
2523 framepos_t const newframe = adjusted_current_frame (event);
2525 framepos_t next = newframe;
2527 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2531 MarkerSelection::iterator i;
2532 list<Location*>::iterator x;
2534 /* find the marker we're dragging, and compute the delta */
2536 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2537 x != _copied_locations.end() && i != _editor->selection->markers.end();
2543 if (marker == _marker) {
2545 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2550 if (real_location->is_mark()) {
2551 f_delta = newframe - copy_location->start();
2555 switch (marker->type()) {
2556 case Marker::SessionStart:
2557 case Marker::RangeStart:
2558 case Marker::LoopStart:
2559 case Marker::PunchIn:
2560 f_delta = newframe - copy_location->start();
2563 case Marker::SessionEnd:
2564 case Marker::RangeEnd:
2565 case Marker::LoopEnd:
2566 case Marker::PunchOut:
2567 f_delta = newframe - copy_location->end();
2570 /* what kind of marker is this ? */
2578 if (i == _editor->selection->markers.end()) {
2579 /* hmm, impossible - we didn't find the dragged marker */
2583 /* now move them all */
2585 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2586 x != _copied_locations.end() && i != _editor->selection->markers.end();
2592 /* call this to find out if its the start or end */
2594 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2598 if (real_location->locked()) {
2602 if (copy_location->is_mark()) {
2606 copy_location->set_start (copy_location->start() + f_delta);
2610 framepos_t new_start = copy_location->start() + f_delta;
2611 framepos_t new_end = copy_location->end() + f_delta;
2613 if (is_start) { // start-of-range marker
2616 copy_location->set_start (new_start);
2617 copy_location->set_end (new_end);
2618 } else if (new_start < copy_location->end()) {
2619 copy_location->set_start (new_start);
2620 } else if (newframe > 0) {
2621 _editor->snap_to (next, 1, true);
2622 copy_location->set_end (next);
2623 copy_location->set_start (newframe);
2626 } else { // end marker
2629 copy_location->set_end (new_end);
2630 copy_location->set_start (new_start);
2631 } else if (new_end > copy_location->start()) {
2632 copy_location->set_end (new_end);
2633 } else if (newframe > 0) {
2634 _editor->snap_to (next, -1, true);
2635 copy_location->set_start (next);
2636 copy_location->set_end (newframe);
2641 update_item (copy_location);
2643 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2646 lm->set_position (copy_location->start(), copy_location->end());
2650 assert (!_copied_locations.empty());
2652 show_verbose_cursor_time (newframe);
2655 _editor->update_canvas_now ();
2660 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2662 if (!movement_occurred) {
2664 /* just a click, do nothing but finish
2665 off the selection process
2668 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2671 case Selection::Set:
2672 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2673 _editor->selection->set (_marker);
2677 case Selection::Toggle:
2678 case Selection::Extend:
2679 case Selection::Add:
2686 _editor->_dragging_edit_point = false;
2688 _editor->begin_reversible_command ( _("move marker") );
2689 XMLNode &before = _editor->session()->locations()->get_state();
2691 MarkerSelection::iterator i;
2692 list<Location*>::iterator x;
2695 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2696 x != _copied_locations.end() && i != _editor->selection->markers.end();
2699 Location * location = _editor->find_location_from_marker (*i, is_start);
2703 if (location->locked()) {
2707 if (location->is_mark()) {
2708 location->set_start ((*x)->start());
2710 location->set ((*x)->start(), (*x)->end());
2715 XMLNode &after = _editor->session()->locations()->get_state();
2716 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2717 _editor->commit_reversible_command ();
2721 MarkerDrag::aborted (bool)
2727 MarkerDrag::update_item (Location*)
2732 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2734 _cumulative_x_drag (0),
2735 _cumulative_y_drag (0)
2737 if (_zero_gain_fraction < 0.0) {
2738 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2741 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2743 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2749 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2751 Drag::start_grab (event, _editor->cursors()->fader);
2753 // start the grab at the center of the control point so
2754 // the point doesn't 'jump' to the mouse after the first drag
2755 _fixed_grab_x = _point->get_x();
2756 _fixed_grab_y = _point->get_y();
2758 float const fraction = 1 - (_point->get_y() / _point->line().height());
2760 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2762 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2763 event->button.x + 10, event->button.y + 10);
2765 _editor->verbose_cursor()->show ();
2769 ControlPointDrag::motion (GdkEvent* event, bool)
2771 double dx = _drags->current_pointer_x() - last_pointer_x();
2772 double dy = _drags->current_pointer_y() - last_pointer_y();
2774 if (event->button.state & Keyboard::SecondaryModifier) {
2779 /* coordinate in pixels relative to the start of the region (for region-based automation)
2780 or track (for track-based automation) */
2781 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2782 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2784 // calculate zero crossing point. back off by .01 to stay on the
2785 // positive side of zero
2786 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2788 // make sure we hit zero when passing through
2789 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2793 if (_x_constrained) {
2796 if (_y_constrained) {
2800 _cumulative_x_drag = cx - _fixed_grab_x;
2801 _cumulative_y_drag = cy - _fixed_grab_y;
2805 cy = min ((double) _point->line().height(), cy);
2807 framepos_t cx_frames = _editor->unit_to_frame (cx);
2809 if (!_x_constrained) {
2810 _editor->snap_to_with_modifier (cx_frames, event);
2813 cx_frames = min (cx_frames, _point->line().maximum_time());
2815 float const fraction = 1.0 - (cy / _point->line().height());
2817 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2819 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2821 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2825 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2827 if (!movement_occurred) {
2831 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2832 _editor->reset_point_selection ();
2836 motion (event, false);
2839 _point->line().end_drag ();
2840 _editor->session()->commit_reversible_command ();
2844 ControlPointDrag::aborted (bool)
2846 _point->line().reset ();
2850 ControlPointDrag::active (Editing::MouseMode m)
2852 if (m == Editing::MouseGain) {
2853 /* always active in mouse gain */
2857 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2858 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2861 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2864 _cumulative_y_drag (0)
2866 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2870 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2872 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2875 _item = &_line->grab_item ();
2877 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2878 origin, and ditto for y.
2881 double cx = event->button.x;
2882 double cy = event->button.y;
2884 _line->parent_group().w2i (cx, cy);
2886 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2891 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2892 /* no adjacent points */
2896 Drag::start_grab (event, _editor->cursors()->fader);
2898 /* store grab start in parent frame */
2903 double fraction = 1.0 - (cy / _line->height());
2905 _line->start_drag_line (before, after, fraction);
2907 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2908 event->button.x + 10, event->button.y + 10);
2910 _editor->verbose_cursor()->show ();
2914 LineDrag::motion (GdkEvent* event, bool)
2916 double dy = _drags->current_pointer_y() - last_pointer_y();
2918 if (event->button.state & Keyboard::SecondaryModifier) {
2922 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2924 _cumulative_y_drag = cy - _fixed_grab_y;
2927 cy = min ((double) _line->height(), cy);
2929 double const fraction = 1.0 - (cy / _line->height());
2933 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2939 /* we are ignoring x position for this drag, so we can just pass in anything */
2940 _line->drag_motion (0, fraction, true, push);
2942 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2946 LineDrag::finished (GdkEvent* event, bool)
2948 motion (event, false);
2950 _editor->session()->commit_reversible_command ();
2954 LineDrag::aborted (bool)
2959 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2962 _cumulative_x_drag (0)
2964 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2968 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2970 Drag::start_grab (event);
2972 _line = reinterpret_cast<Line*> (_item);
2975 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2977 double cx = event->button.x;
2978 double cy = event->button.y;
2980 _item->property_parent().get_value()->w2i(cx, cy);
2982 /* store grab start in parent frame */
2983 _region_view_grab_x = cx;
2985 _before = *(float*) _item->get_data ("position");
2987 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2989 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2993 FeatureLineDrag::motion (GdkEvent*, bool)
2995 double dx = _drags->current_pointer_x() - last_pointer_x();
2997 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2999 _cumulative_x_drag += dx;
3001 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3010 ArdourCanvas::Points points;
3012 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3014 _line->get_bounds(x1, y2, x2, y2);
3016 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3017 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3019 _line->property_points() = points;
3021 float *pos = new float;
3024 _line->set_data ("position", pos);
3030 FeatureLineDrag::finished (GdkEvent*, bool)
3032 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3033 _arv->update_transient(_before, _before);
3037 FeatureLineDrag::aborted (bool)
3042 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3045 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3049 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3051 Drag::start_grab (event);
3052 show_verbose_cursor_time (adjusted_current_frame (event));
3056 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3063 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3065 framepos_t grab = grab_frame ();
3066 if (Config->get_rubberbanding_snaps_to_grid ()) {
3067 _editor->snap_to_with_modifier (grab, event);
3070 /* base start and end on initial click position */
3080 if (_drags->current_pointer_y() < grab_y()) {
3081 y1 = _drags->current_pointer_y();
3084 y2 = _drags->current_pointer_y();
3089 if (start != end || y1 != y2) {
3091 double x1 = _editor->frame_to_pixel (start);
3092 double x2 = _editor->frame_to_pixel (end);
3094 _editor->rubberband_rect->property_x1() = x1;
3095 _editor->rubberband_rect->property_y1() = y1;
3096 _editor->rubberband_rect->property_x2() = x2;
3097 _editor->rubberband_rect->property_y2() = y2;
3099 _editor->rubberband_rect->show();
3100 _editor->rubberband_rect->raise_to_top();
3102 show_verbose_cursor_time (pf);
3104 do_select_things (event, true);
3109 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3114 if (grab_frame() < last_pointer_frame()) {
3116 x2 = last_pointer_frame ();
3119 x1 = last_pointer_frame ();
3125 if (_drags->current_pointer_y() < grab_y()) {
3126 y1 = _drags->current_pointer_y();
3129 y2 = _drags->current_pointer_y();
3133 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3137 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3139 if (movement_occurred) {
3141 motion (event, false);
3142 do_select_things (event, false);
3150 _editor->rubberband_rect->hide();
3154 RubberbandSelectDrag::aborted (bool)
3156 _editor->rubberband_rect->hide ();
3159 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3160 : RegionDrag (e, i, p, v)
3162 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3166 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3168 Drag::start_grab (event, cursor);
3170 show_verbose_cursor_time (adjusted_current_frame (event));
3174 TimeFXDrag::motion (GdkEvent* event, bool)
3176 RegionView* rv = _primary;
3178 framepos_t const pf = adjusted_current_frame (event);
3180 if (pf > rv->region()->position()) {
3181 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3184 show_verbose_cursor_time (pf);
3188 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3190 _primary->get_time_axis_view().hide_timestretch ();
3192 if (!movement_occurred) {
3196 if (last_pointer_frame() < _primary->region()->position()) {
3197 /* backwards drag of the left edge - not usable */
3201 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3203 float percentage = (double) newlen / (double) _primary->region()->length();
3205 #ifndef USE_RUBBERBAND
3206 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3207 if (_primary->region()->data_type() == DataType::AUDIO) {
3208 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3212 // XXX how do timeFX on multiple regions ?
3217 if (_editor->time_stretch (rs, percentage) == -1) {
3218 error << _("An error occurred while executing time stretch operation") << endmsg;
3223 TimeFXDrag::aborted (bool)
3225 _primary->get_time_axis_view().hide_timestretch ();
3228 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3231 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3235 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3237 Drag::start_grab (event);
3241 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3243 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3247 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3249 if (movement_occurred && _editor->session()) {
3250 /* make sure we stop */
3251 _editor->session()->request_transport_speed (0.0);
3256 ScrubDrag::aborted (bool)
3261 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3265 , _original_pointer_time_axis (-1)
3266 , _last_pointer_time_axis (-1)
3268 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3272 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3274 if (_editor->session() == 0) {
3278 Gdk::Cursor* cursor = 0;
3280 switch (_operation) {
3281 case CreateSelection:
3282 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3287 cursor = _editor->cursors()->selector;
3288 Drag::start_grab (event, cursor);
3291 case SelectionStartTrim:
3292 if (_editor->clicked_axisview) {
3293 _editor->clicked_axisview->order_selection_trims (_item, true);
3295 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3298 case SelectionEndTrim:
3299 if (_editor->clicked_axisview) {
3300 _editor->clicked_axisview->order_selection_trims (_item, false);
3302 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3306 Drag::start_grab (event, cursor);
3310 if (_operation == SelectionMove) {
3311 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3313 show_verbose_cursor_time (adjusted_current_frame (event));
3316 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3320 SelectionDrag::setup_pointer_frame_offset ()
3322 switch (_operation) {
3323 case CreateSelection:
3324 _pointer_frame_offset = 0;
3327 case SelectionStartTrim:
3329 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3332 case SelectionEndTrim:
3333 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3339 SelectionDrag::motion (GdkEvent* event, bool first_move)
3341 framepos_t start = 0;
3345 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3346 if (pending_time_axis.first == 0) {
3350 framepos_t const pending_position = adjusted_current_frame (event);
3352 /* only alter selection if things have changed */
3354 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3358 switch (_operation) {
3359 case CreateSelection:
3361 framepos_t grab = grab_frame ();
3364 _editor->snap_to (grab);
3367 if (pending_position < grab_frame()) {
3368 start = pending_position;
3371 end = pending_position;
3375 /* first drag: Either add to the selection
3376 or create a new selection
3382 /* adding to the selection */
3383 _editor->set_selected_track_as_side_effect (Selection::Add);
3384 //_editor->selection->add (_editor->clicked_axisview);
3385 _editor->clicked_selection = _editor->selection->add (start, end);
3390 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3391 //_editor->selection->set (_editor->clicked_axisview);
3392 _editor->set_selected_track_as_side_effect (Selection::Set);
3395 _editor->clicked_selection = _editor->selection->set (start, end);
3399 /* select the track that we're in */
3400 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3401 // _editor->set_selected_track_as_side_effect (Selection::Add);
3402 _editor->selection->add (pending_time_axis.first);
3403 _added_time_axes.push_back (pending_time_axis.first);
3406 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3407 tracks that we selected in the first place.
3410 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3411 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3413 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3414 while (i != _added_time_axes.end()) {
3416 list<TimeAxisView*>::iterator tmp = i;
3419 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3420 _editor->selection->remove (*i);
3421 _added_time_axes.remove (*i);
3430 case SelectionStartTrim:
3432 start = _editor->selection->time[_editor->clicked_selection].start;
3433 end = _editor->selection->time[_editor->clicked_selection].end;
3435 if (pending_position > end) {
3438 start = pending_position;
3442 case SelectionEndTrim:
3444 start = _editor->selection->time[_editor->clicked_selection].start;
3445 end = _editor->selection->time[_editor->clicked_selection].end;
3447 if (pending_position < start) {
3450 end = pending_position;
3457 start = _editor->selection->time[_editor->clicked_selection].start;
3458 end = _editor->selection->time[_editor->clicked_selection].end;
3460 length = end - start;
3462 start = pending_position;
3463 _editor->snap_to (start);
3465 end = start + length;
3470 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3471 _editor->start_canvas_autoscroll (1, 0);
3475 _editor->selection->replace (_editor->clicked_selection, start, end);
3478 if (_operation == SelectionMove) {
3479 show_verbose_cursor_time(start);
3481 show_verbose_cursor_time(pending_position);
3486 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3488 Session* s = _editor->session();
3490 if (movement_occurred) {
3491 motion (event, false);
3492 /* XXX this is not object-oriented programming at all. ick */
3493 if (_editor->selection->time.consolidate()) {
3494 _editor->selection->TimeChanged ();
3497 /* XXX what if its a music time selection? */
3498 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3499 s->request_play_range (&_editor->selection->time, true);
3504 /* just a click, no pointer movement.*/
3506 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3507 _editor->selection->clear_time();
3510 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3511 _editor->selection->set (_editor->clicked_axisview);
3514 if (s && s->get_play_range () && s->transport_rolling()) {
3515 s->request_stop (false, false);
3520 _editor->stop_canvas_autoscroll ();
3524 SelectionDrag::aborted (bool)
3529 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3534 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3536 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3537 physical_screen_height (_editor->get_window()));
3538 _drag_rect->hide ();
3540 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3541 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3545 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3547 if (_editor->session() == 0) {
3551 Gdk::Cursor* cursor = 0;
3553 if (!_editor->temp_location) {
3554 _editor->temp_location = new Location (*_editor->session());
3557 switch (_operation) {
3558 case CreateRangeMarker:
3559 case CreateTransportMarker:
3560 case CreateCDMarker:
3562 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3567 cursor = _editor->cursors()->selector;
3571 Drag::start_grab (event, cursor);
3573 show_verbose_cursor_time (adjusted_current_frame (event));
3577 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3579 framepos_t start = 0;
3581 ArdourCanvas::SimpleRect *crect;
3583 switch (_operation) {
3584 case CreateRangeMarker:
3585 crect = _editor->range_bar_drag_rect;
3587 case CreateTransportMarker:
3588 crect = _editor->transport_bar_drag_rect;
3590 case CreateCDMarker:
3591 crect = _editor->cd_marker_bar_drag_rect;
3594 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3599 framepos_t const pf = adjusted_current_frame (event);
3601 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3602 framepos_t grab = grab_frame ();
3603 _editor->snap_to (grab);
3605 if (pf < grab_frame()) {
3613 /* first drag: Either add to the selection
3614 or create a new selection.
3619 _editor->temp_location->set (start, end);
3623 update_item (_editor->temp_location);
3625 //_drag_rect->raise_to_top();
3630 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3631 _editor->start_canvas_autoscroll (1, 0);
3635 _editor->temp_location->set (start, end);
3637 double x1 = _editor->frame_to_pixel (start);
3638 double x2 = _editor->frame_to_pixel (end);
3639 crect->property_x1() = x1;
3640 crect->property_x2() = x2;
3642 update_item (_editor->temp_location);
3645 show_verbose_cursor_time (pf);
3650 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3652 Location * newloc = 0;
3656 if (movement_occurred) {
3657 motion (event, false);
3660 switch (_operation) {
3661 case CreateRangeMarker:
3662 case CreateCDMarker:
3664 _editor->begin_reversible_command (_("new range marker"));
3665 XMLNode &before = _editor->session()->locations()->get_state();
3666 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3667 if (_operation == CreateCDMarker) {
3668 flags = Location::IsRangeMarker | Location::IsCDMarker;
3669 _editor->cd_marker_bar_drag_rect->hide();
3672 flags = Location::IsRangeMarker;
3673 _editor->range_bar_drag_rect->hide();
3675 newloc = new Location (
3676 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3679 _editor->session()->locations()->add (newloc, true);
3680 XMLNode &after = _editor->session()->locations()->get_state();
3681 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3682 _editor->commit_reversible_command ();
3686 case CreateTransportMarker:
3687 // popup menu to pick loop or punch
3688 _editor->new_transport_marker_context_menu (&event->button, _item);
3692 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3694 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3699 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3701 if (end == max_framepos) {
3702 end = _editor->session()->current_end_frame ();
3705 if (start == max_framepos) {
3706 start = _editor->session()->current_start_frame ();
3709 switch (_editor->mouse_mode) {
3711 /* find the two markers on either side and then make the selection from it */
3712 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3716 /* find the two markers on either side of the click and make the range out of it */
3717 _editor->selection->set (start, end);
3726 _editor->stop_canvas_autoscroll ();
3730 RangeMarkerBarDrag::aborted (bool)
3736 RangeMarkerBarDrag::update_item (Location* location)
3738 double const x1 = _editor->frame_to_pixel (location->start());
3739 double const x2 = _editor->frame_to_pixel (location->end());
3741 _drag_rect->property_x1() = x1;
3742 _drag_rect->property_x2() = x2;
3745 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3749 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3753 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3755 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3756 Drag::start_grab (event, _editor->cursors()->zoom_out);
3759 Drag::start_grab (event, _editor->cursors()->zoom_in);
3763 show_verbose_cursor_time (adjusted_current_frame (event));
3767 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3772 framepos_t const pf = adjusted_current_frame (event);
3774 framepos_t grab = grab_frame ();
3775 _editor->snap_to_with_modifier (grab, event);
3777 /* base start and end on initial click position */
3789 _editor->zoom_rect->show();
3790 _editor->zoom_rect->raise_to_top();
3793 _editor->reposition_zoom_rect(start, end);
3795 show_verbose_cursor_time (pf);
3800 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3802 if (movement_occurred) {
3803 motion (event, false);
3805 if (grab_frame() < last_pointer_frame()) {
3806 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3808 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3811 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3812 _editor->tav_zoom_step (_zoom_out);
3814 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3818 _editor->zoom_rect->hide();
3822 MouseZoomDrag::aborted (bool)
3824 _editor->zoom_rect->hide ();
3827 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3829 , _cumulative_dx (0)
3830 , _cumulative_dy (0)
3832 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3834 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3835 _region = &_primary->region_view ();
3836 _note_height = _region->midi_stream_view()->note_height ();
3840 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3842 Drag::start_grab (event);
3844 if (!(_was_selected = _primary->selected())) {
3846 /* tertiary-click means extend selection - we'll do that on button release,
3847 so don't add it here, because otherwise we make it hard to figure
3848 out the "extend-to" range.
3851 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3854 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3857 _region->note_selected (_primary, true);
3859 _region->unique_select (_primary);
3865 /** @return Current total drag x change in frames */
3867 NoteDrag::total_dx () const
3870 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3872 /* primary note time */
3873 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3875 /* new time of the primary note in session frames */
3876 frameoffset_t st = n + dx;
3878 framepos_t const rp = _region->region()->position ();
3880 /* prevent the note being dragged earlier than the region's position */
3883 /* snap and return corresponding delta */
3884 return _region->snap_frame_to_frame (st - rp) + rp - n;
3887 /** @return Current total drag y change in note number */
3889 NoteDrag::total_dy () const
3891 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3895 NoteDrag::motion (GdkEvent *, bool)
3897 /* Total change in x and y since the start of the drag */
3898 frameoffset_t const dx = total_dx ();
3899 int8_t const dy = total_dy ();
3901 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3902 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3903 double const tdy = -dy * _note_height - _cumulative_dy;
3906 _cumulative_dx += tdx;
3907 _cumulative_dy += tdy;
3909 int8_t note_delta = total_dy();
3911 _region->move_selection (tdx, tdy, note_delta);
3913 /* the new note value may be the same as the old one, but we
3914 * don't know what that means because the selection may have
3915 * involved more than one note and we might be doing something
3916 * odd with them. so show the note value anyway, always.
3920 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3922 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3923 (int) floor (new_note));
3925 show_verbose_cursor_text (buf);
3930 NoteDrag::finished (GdkEvent* ev, bool moved)
3933 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3935 if (_was_selected) {
3936 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3938 _region->note_deselected (_primary);
3941 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3942 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3944 if (!extend && !add && _region->selection_size() > 1) {
3945 _region->unique_select (_primary);
3946 } else if (extend) {
3947 _region->note_selected (_primary, true, true);
3949 /* it was added during button press */
3954 _region->note_dropped (_primary, total_dx(), total_dy());
3959 NoteDrag::aborted (bool)
3964 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3965 : Drag (editor, item)
3967 , _nothing_to_drag (false)
3969 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3971 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3974 /* get all lines in the automation view */
3975 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3977 /* find those that overlap the ranges being dragged */
3978 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3979 while (i != lines.end ()) {
3980 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3983 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3985 /* check this range against all the AudioRanges that we are using */
3986 list<AudioRange>::const_iterator k = _ranges.begin ();
3987 while (k != _ranges.end()) {
3988 if (k->coverage (r.first, r.second) != OverlapNone) {
3994 /* add it to our list if it overlaps at all */
3995 if (k != _ranges.end()) {
4000 _lines.push_back (n);
4006 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4010 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4012 Drag::start_grab (event, cursor);
4014 /* Get line states before we start changing things */
4015 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4016 i->state = &i->line->get_state ();
4019 if (_ranges.empty()) {
4021 /* No selected time ranges: drag all points */
4022 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4023 uint32_t const N = i->line->npoints ();
4024 for (uint32_t j = 0; j < N; ++j) {
4025 i->points.push_back (i->line->nth (j));
4031 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4033 framecnt_t const half = (i->start + i->end) / 2;
4035 /* find the line that this audio range starts in */
4036 list<Line>::iterator j = _lines.begin();
4037 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4041 if (j != _lines.end()) {
4042 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4044 /* j is the line that this audio range starts in; fade into it;
4045 64 samples length plucked out of thin air.
4048 framepos_t a = i->start + 64;
4053 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4054 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4056 the_list->add (p, the_list->eval (p));
4057 j->line->add_always_in_view (p);
4058 the_list->add (q, the_list->eval (q));
4059 j->line->add_always_in_view (q);
4062 /* same thing for the end */
4065 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4069 if (j != _lines.end()) {
4070 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4072 /* j is the line that this audio range starts in; fade out of it;
4073 64 samples length plucked out of thin air.
4076 framepos_t b = i->end - 64;
4081 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4082 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4084 the_list->add (p, the_list->eval (p));
4085 j->line->add_always_in_view (p);
4086 the_list->add (q, the_list->eval (q));
4087 j->line->add_always_in_view (q);
4091 _nothing_to_drag = true;
4093 /* Find all the points that should be dragged and put them in the relevant
4094 points lists in the Line structs.
4097 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4099 uint32_t const N = i->line->npoints ();
4100 for (uint32_t j = 0; j < N; ++j) {
4102 /* here's a control point on this line */
4103 ControlPoint* p = i->line->nth (j);
4104 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4106 /* see if it's inside a range */
4107 list<AudioRange>::const_iterator k = _ranges.begin ();
4108 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4112 if (k != _ranges.end()) {
4113 /* dragging this point */
4114 _nothing_to_drag = false;
4115 i->points.push_back (p);
4121 if (_nothing_to_drag) {
4125 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4126 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4131 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4133 if (_nothing_to_drag) {
4137 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4138 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4140 /* we are ignoring x position for this drag, so we can just pass in anything */
4141 i->line->drag_motion (0, f, true, false);
4146 AutomationRangeDrag::finished (GdkEvent* event, bool)
4148 if (_nothing_to_drag) {
4152 motion (event, false);
4153 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4154 i->line->end_drag ();
4155 i->line->clear_always_in_view ();
4158 _editor->session()->commit_reversible_command ();
4162 AutomationRangeDrag::aborted (bool)
4164 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4165 i->line->clear_always_in_view ();
4170 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4173 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4174 layer = v->region()->layer ();
4175 initial_y = v->get_canvas_group()->property_y ();
4176 initial_playlist = v->region()->playlist ();
4177 initial_position = v->region()->position ();
4178 initial_end = v->region()->position () + v->region()->length ();
4181 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4185 , _cumulative_dx (0)
4187 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4191 PatchChangeDrag::motion (GdkEvent* ev, bool)
4193 framepos_t f = adjusted_current_frame (ev);
4194 boost::shared_ptr<Region> r = _region_view->region ();
4195 f = max (f, r->position ());
4196 f = min (f, r->last_frame ());
4198 framecnt_t const dxf = f - grab_frame();
4199 double const dxu = _editor->frame_to_unit (dxf);
4200 _patch_change->move (dxu - _cumulative_dx, 0);
4201 _cumulative_dx = dxu;
4205 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4207 if (!movement_occurred) {
4211 boost::shared_ptr<Region> r (_region_view->region ());
4213 framepos_t f = adjusted_current_frame (ev);
4214 f = max (f, r->position ());
4215 f = min (f, r->last_frame ());
4217 _region_view->move_patch_change (
4219 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4224 PatchChangeDrag::aborted (bool)
4226 _patch_change->move (-_cumulative_dx, 0);
4230 PatchChangeDrag::setup_pointer_frame_offset ()
4232 boost::shared_ptr<Region> region = _region_view->region ();
4233 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4236 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4237 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4244 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4246 framepos_t const p = _region_view->region()->position ();
4247 double const y = _region_view->midi_view()->y_position ();
4249 x1 = max ((framepos_t) 0, x1 - p);
4250 x2 = max ((framepos_t) 0, x2 - p);
4251 y1 = max (0.0, y1 - y);
4252 y2 = max (0.0, y2 - y);
4254 _region_view->update_drag_selection (
4255 _editor->frame_to_pixel (x1),
4256 _editor->frame_to_pixel (x2),
4259 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4264 MidiRubberbandSelectDrag::deselect_things ()
4269 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4270 : RubberbandSelectDrag (e, i)
4276 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4278 if (drag_in_progress) {
4279 /* We just want to select things at the end of the drag, not during it */
4283 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4285 _editor->begin_reversible_command (_("rubberband selection"));
4286 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4287 _editor->commit_reversible_command ();
4291 EditorRubberbandSelectDrag::deselect_things ()
4293 if (!getenv("ARDOUR_SAE")) {
4294 _editor->selection->clear_tracks();
4296 _editor->selection->clear_regions();
4297 _editor->selection->clear_points ();
4298 _editor->selection->clear_lines ();