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);
3107 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3109 if (movement_occurred) {
3111 motion (event, false);
3114 if (_drags->current_pointer_y() < grab_y()) {
3115 y1 = _drags->current_pointer_y();
3118 y2 = _drags->current_pointer_y();
3123 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3125 _editor->begin_reversible_command (_("rubberband selection"));
3127 if (grab_frame() < last_pointer_frame()) {
3128 _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3130 _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3133 _editor->commit_reversible_command ();
3136 if (!getenv("ARDOUR_SAE")) {
3137 _editor->selection->clear_tracks();
3139 _editor->selection->clear_regions();
3140 _editor->selection->clear_points ();
3141 _editor->selection->clear_lines ();
3144 _editor->rubberband_rect->hide();
3148 RubberbandSelectDrag::aborted (bool)
3150 _editor->rubberband_rect->hide ();
3153 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3154 : RegionDrag (e, i, p, v)
3156 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3160 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3162 Drag::start_grab (event, cursor);
3164 show_verbose_cursor_time (adjusted_current_frame (event));
3168 TimeFXDrag::motion (GdkEvent* event, bool)
3170 RegionView* rv = _primary;
3172 framepos_t const pf = adjusted_current_frame (event);
3174 if (pf > rv->region()->position()) {
3175 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3178 show_verbose_cursor_time (pf);
3182 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3184 _primary->get_time_axis_view().hide_timestretch ();
3186 if (!movement_occurred) {
3190 if (last_pointer_frame() < _primary->region()->position()) {
3191 /* backwards drag of the left edge - not usable */
3195 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3197 float percentage = (double) newlen / (double) _primary->region()->length();
3199 #ifndef USE_RUBBERBAND
3200 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3201 if (_primary->region()->data_type() == DataType::AUDIO) {
3202 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3206 // XXX how do timeFX on multiple regions ?
3211 if (_editor->time_stretch (rs, percentage) == -1) {
3212 error << _("An error occurred while executing time stretch operation") << endmsg;
3217 TimeFXDrag::aborted (bool)
3219 _primary->get_time_axis_view().hide_timestretch ();
3222 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3225 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3229 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3231 Drag::start_grab (event);
3235 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3237 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3241 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3243 if (movement_occurred && _editor->session()) {
3244 /* make sure we stop */
3245 _editor->session()->request_transport_speed (0.0);
3250 ScrubDrag::aborted (bool)
3255 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3259 , _original_pointer_time_axis (-1)
3260 , _last_pointer_time_axis (-1)
3262 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3266 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3268 if (_editor->session() == 0) {
3272 Gdk::Cursor* cursor = 0;
3274 switch (_operation) {
3275 case CreateSelection:
3276 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3281 cursor = _editor->cursors()->selector;
3282 Drag::start_grab (event, cursor);
3285 case SelectionStartTrim:
3286 if (_editor->clicked_axisview) {
3287 _editor->clicked_axisview->order_selection_trims (_item, true);
3289 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3292 case SelectionEndTrim:
3293 if (_editor->clicked_axisview) {
3294 _editor->clicked_axisview->order_selection_trims (_item, false);
3296 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3300 Drag::start_grab (event, cursor);
3304 if (_operation == SelectionMove) {
3305 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3307 show_verbose_cursor_time (adjusted_current_frame (event));
3310 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3314 SelectionDrag::setup_pointer_frame_offset ()
3316 switch (_operation) {
3317 case CreateSelection:
3318 _pointer_frame_offset = 0;
3321 case SelectionStartTrim:
3323 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3326 case SelectionEndTrim:
3327 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3333 SelectionDrag::motion (GdkEvent* event, bool first_move)
3335 framepos_t start = 0;
3339 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3340 if (pending_time_axis.first == 0) {
3344 framepos_t const pending_position = adjusted_current_frame (event);
3346 /* only alter selection if things have changed */
3348 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3352 switch (_operation) {
3353 case CreateSelection:
3355 framepos_t grab = grab_frame ();
3358 _editor->snap_to (grab);
3361 if (pending_position < grab_frame()) {
3362 start = pending_position;
3365 end = pending_position;
3369 /* first drag: Either add to the selection
3370 or create a new selection
3376 /* adding to the selection */
3377 _editor->set_selected_track_as_side_effect (Selection::Add);
3378 //_editor->selection->add (_editor->clicked_axisview);
3379 _editor->clicked_selection = _editor->selection->add (start, end);
3384 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3385 //_editor->selection->set (_editor->clicked_axisview);
3386 _editor->set_selected_track_as_side_effect (Selection::Set);
3389 _editor->clicked_selection = _editor->selection->set (start, end);
3393 /* select the track that we're in */
3394 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3395 // _editor->set_selected_track_as_side_effect (Selection::Add);
3396 _editor->selection->add (pending_time_axis.first);
3397 _added_time_axes.push_back (pending_time_axis.first);
3400 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3401 tracks that we selected in the first place.
3404 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3405 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3407 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3408 while (i != _added_time_axes.end()) {
3410 list<TimeAxisView*>::iterator tmp = i;
3413 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3414 _editor->selection->remove (*i);
3415 _added_time_axes.remove (*i);
3424 case SelectionStartTrim:
3426 start = _editor->selection->time[_editor->clicked_selection].start;
3427 end = _editor->selection->time[_editor->clicked_selection].end;
3429 if (pending_position > end) {
3432 start = pending_position;
3436 case SelectionEndTrim:
3438 start = _editor->selection->time[_editor->clicked_selection].start;
3439 end = _editor->selection->time[_editor->clicked_selection].end;
3441 if (pending_position < start) {
3444 end = pending_position;
3451 start = _editor->selection->time[_editor->clicked_selection].start;
3452 end = _editor->selection->time[_editor->clicked_selection].end;
3454 length = end - start;
3456 start = pending_position;
3457 _editor->snap_to (start);
3459 end = start + length;
3464 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3465 _editor->start_canvas_autoscroll (1, 0);
3469 _editor->selection->replace (_editor->clicked_selection, start, end);
3472 if (_operation == SelectionMove) {
3473 show_verbose_cursor_time(start);
3475 show_verbose_cursor_time(pending_position);
3480 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3482 Session* s = _editor->session();
3484 if (movement_occurred) {
3485 motion (event, false);
3486 /* XXX this is not object-oriented programming at all. ick */
3487 if (_editor->selection->time.consolidate()) {
3488 _editor->selection->TimeChanged ();
3491 /* XXX what if its a music time selection? */
3492 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3493 s->request_play_range (&_editor->selection->time, true);
3498 /* just a click, no pointer movement.*/
3500 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3501 _editor->selection->clear_time();
3504 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3505 _editor->selection->set (_editor->clicked_axisview);
3508 if (s && s->get_play_range () && s->transport_rolling()) {
3509 s->request_stop (false, false);
3514 _editor->stop_canvas_autoscroll ();
3518 SelectionDrag::aborted (bool)
3523 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3528 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3530 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3531 physical_screen_height (_editor->get_window()));
3532 _drag_rect->hide ();
3534 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3535 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3539 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3541 if (_editor->session() == 0) {
3545 Gdk::Cursor* cursor = 0;
3547 if (!_editor->temp_location) {
3548 _editor->temp_location = new Location (*_editor->session());
3551 switch (_operation) {
3552 case CreateRangeMarker:
3553 case CreateTransportMarker:
3554 case CreateCDMarker:
3556 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3561 cursor = _editor->cursors()->selector;
3565 Drag::start_grab (event, cursor);
3567 show_verbose_cursor_time (adjusted_current_frame (event));
3571 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3573 framepos_t start = 0;
3575 ArdourCanvas::SimpleRect *crect;
3577 switch (_operation) {
3578 case CreateRangeMarker:
3579 crect = _editor->range_bar_drag_rect;
3581 case CreateTransportMarker:
3582 crect = _editor->transport_bar_drag_rect;
3584 case CreateCDMarker:
3585 crect = _editor->cd_marker_bar_drag_rect;
3588 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3593 framepos_t const pf = adjusted_current_frame (event);
3595 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3596 framepos_t grab = grab_frame ();
3597 _editor->snap_to (grab);
3599 if (pf < grab_frame()) {
3607 /* first drag: Either add to the selection
3608 or create a new selection.
3613 _editor->temp_location->set (start, end);
3617 update_item (_editor->temp_location);
3619 //_drag_rect->raise_to_top();
3624 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3625 _editor->start_canvas_autoscroll (1, 0);
3629 _editor->temp_location->set (start, end);
3631 double x1 = _editor->frame_to_pixel (start);
3632 double x2 = _editor->frame_to_pixel (end);
3633 crect->property_x1() = x1;
3634 crect->property_x2() = x2;
3636 update_item (_editor->temp_location);
3639 show_verbose_cursor_time (pf);
3644 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3646 Location * newloc = 0;
3650 if (movement_occurred) {
3651 motion (event, false);
3654 switch (_operation) {
3655 case CreateRangeMarker:
3656 case CreateCDMarker:
3658 _editor->begin_reversible_command (_("new range marker"));
3659 XMLNode &before = _editor->session()->locations()->get_state();
3660 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3661 if (_operation == CreateCDMarker) {
3662 flags = Location::IsRangeMarker | Location::IsCDMarker;
3663 _editor->cd_marker_bar_drag_rect->hide();
3666 flags = Location::IsRangeMarker;
3667 _editor->range_bar_drag_rect->hide();
3669 newloc = new Location (
3670 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3673 _editor->session()->locations()->add (newloc, true);
3674 XMLNode &after = _editor->session()->locations()->get_state();
3675 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3676 _editor->commit_reversible_command ();
3680 case CreateTransportMarker:
3681 // popup menu to pick loop or punch
3682 _editor->new_transport_marker_context_menu (&event->button, _item);
3686 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3688 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3693 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3695 if (end == max_framepos) {
3696 end = _editor->session()->current_end_frame ();
3699 if (start == max_framepos) {
3700 start = _editor->session()->current_start_frame ();
3703 switch (_editor->mouse_mode) {
3705 /* find the two markers on either side and then make the selection from it */
3706 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3710 /* find the two markers on either side of the click and make the range out of it */
3711 _editor->selection->set (start, end);
3720 _editor->stop_canvas_autoscroll ();
3724 RangeMarkerBarDrag::aborted (bool)
3730 RangeMarkerBarDrag::update_item (Location* location)
3732 double const x1 = _editor->frame_to_pixel (location->start());
3733 double const x2 = _editor->frame_to_pixel (location->end());
3735 _drag_rect->property_x1() = x1;
3736 _drag_rect->property_x2() = x2;
3739 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3743 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3747 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3749 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3750 Drag::start_grab (event, _editor->cursors()->zoom_out);
3753 Drag::start_grab (event, _editor->cursors()->zoom_in);
3757 show_verbose_cursor_time (adjusted_current_frame (event));
3761 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3766 framepos_t const pf = adjusted_current_frame (event);
3768 framepos_t grab = grab_frame ();
3769 _editor->snap_to_with_modifier (grab, event);
3771 /* base start and end on initial click position */
3783 _editor->zoom_rect->show();
3784 _editor->zoom_rect->raise_to_top();
3787 _editor->reposition_zoom_rect(start, end);
3789 show_verbose_cursor_time (pf);
3794 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3796 if (movement_occurred) {
3797 motion (event, false);
3799 if (grab_frame() < last_pointer_frame()) {
3800 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3802 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3805 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3806 _editor->tav_zoom_step (_zoom_out);
3808 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3812 _editor->zoom_rect->hide();
3816 MouseZoomDrag::aborted (bool)
3818 _editor->zoom_rect->hide ();
3821 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3823 , _cumulative_dx (0)
3824 , _cumulative_dy (0)
3826 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3828 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3829 _region = &_primary->region_view ();
3830 _note_height = _region->midi_stream_view()->note_height ();
3834 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3836 Drag::start_grab (event);
3838 if (!(_was_selected = _primary->selected())) {
3840 /* tertiary-click means extend selection - we'll do that on button release,
3841 so don't add it here, because otherwise we make it hard to figure
3842 out the "extend-to" range.
3845 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3848 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3851 _region->note_selected (_primary, true);
3853 _region->unique_select (_primary);
3859 /** @return Current total drag x change in frames */
3861 NoteDrag::total_dx () const
3864 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3866 /* primary note time */
3867 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3869 /* new time of the primary note in session frames */
3870 frameoffset_t st = n + dx;
3872 framepos_t const rp = _region->region()->position ();
3874 /* prevent the note being dragged earlier than the region's position */
3877 /* snap and return corresponding delta */
3878 return _region->snap_frame_to_frame (st - rp) + rp - n;
3881 /** @return Current total drag y change in note number */
3883 NoteDrag::total_dy () const
3885 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3889 NoteDrag::motion (GdkEvent *, bool)
3891 /* Total change in x and y since the start of the drag */
3892 frameoffset_t const dx = total_dx ();
3893 int8_t const dy = total_dy ();
3895 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3896 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3897 double const tdy = -dy * _note_height - _cumulative_dy;
3900 _cumulative_dx += tdx;
3901 _cumulative_dy += tdy;
3903 int8_t note_delta = total_dy();
3905 _region->move_selection (tdx, tdy, note_delta);
3907 /* the new note value may be the same as the old one, but we
3908 * don't know what that means because the selection may have
3909 * involved more than one note and we might be doing something
3910 * odd with them. so show the note value anyway, always.
3914 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3916 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3917 (int) floor (new_note));
3919 show_verbose_cursor_text (buf);
3924 NoteDrag::finished (GdkEvent* ev, bool moved)
3927 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3929 if (_was_selected) {
3930 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3932 _region->note_deselected (_primary);
3935 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3936 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3938 if (!extend && !add && _region->selection_size() > 1) {
3939 _region->unique_select (_primary);
3940 } else if (extend) {
3941 _region->note_selected (_primary, true, true);
3943 /* it was added during button press */
3948 _region->note_dropped (_primary, total_dx(), total_dy());
3953 NoteDrag::aborted (bool)
3958 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3959 : Drag (editor, item)
3961 , _nothing_to_drag (false)
3963 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3965 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3968 /* get all lines in the automation view */
3969 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3971 /* find those that overlap the ranges being dragged */
3972 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3973 while (i != lines.end ()) {
3974 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3977 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3979 /* check this range against all the AudioRanges that we are using */
3980 list<AudioRange>::const_iterator k = _ranges.begin ();
3981 while (k != _ranges.end()) {
3982 if (k->coverage (r.first, r.second) != OverlapNone) {
3988 /* add it to our list if it overlaps at all */
3989 if (k != _ranges.end()) {
3994 _lines.push_back (n);
4000 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4004 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4006 Drag::start_grab (event, cursor);
4008 /* Get line states before we start changing things */
4009 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4010 i->state = &i->line->get_state ();
4013 if (_ranges.empty()) {
4015 /* No selected time ranges: drag all points */
4016 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4017 uint32_t const N = i->line->npoints ();
4018 for (uint32_t j = 0; j < N; ++j) {
4019 i->points.push_back (i->line->nth (j));
4025 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4027 framecnt_t const half = (i->start + i->end) / 2;
4029 /* find the line that this audio range starts in */
4030 list<Line>::iterator j = _lines.begin();
4031 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4035 if (j != _lines.end()) {
4036 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4038 /* j is the line that this audio range starts in; fade into it;
4039 64 samples length plucked out of thin air.
4042 framepos_t a = i->start + 64;
4047 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4048 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4050 the_list->add (p, the_list->eval (p));
4051 j->line->add_always_in_view (p);
4052 the_list->add (q, the_list->eval (q));
4053 j->line->add_always_in_view (q);
4056 /* same thing for the end */
4059 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4063 if (j != _lines.end()) {
4064 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4066 /* j is the line that this audio range starts in; fade out of it;
4067 64 samples length plucked out of thin air.
4070 framepos_t b = i->end - 64;
4075 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4076 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4078 the_list->add (p, the_list->eval (p));
4079 j->line->add_always_in_view (p);
4080 the_list->add (q, the_list->eval (q));
4081 j->line->add_always_in_view (q);
4085 _nothing_to_drag = true;
4087 /* Find all the points that should be dragged and put them in the relevant
4088 points lists in the Line structs.
4091 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4093 uint32_t const N = i->line->npoints ();
4094 for (uint32_t j = 0; j < N; ++j) {
4096 /* here's a control point on this line */
4097 ControlPoint* p = i->line->nth (j);
4098 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4100 /* see if it's inside a range */
4101 list<AudioRange>::const_iterator k = _ranges.begin ();
4102 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4106 if (k != _ranges.end()) {
4107 /* dragging this point */
4108 _nothing_to_drag = false;
4109 i->points.push_back (p);
4115 if (_nothing_to_drag) {
4119 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4120 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4125 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4127 if (_nothing_to_drag) {
4131 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4132 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4134 /* we are ignoring x position for this drag, so we can just pass in anything */
4135 i->line->drag_motion (0, f, true, false);
4140 AutomationRangeDrag::finished (GdkEvent* event, bool)
4142 if (_nothing_to_drag) {
4146 motion (event, false);
4147 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4148 i->line->end_drag ();
4149 i->line->clear_always_in_view ();
4152 _editor->session()->commit_reversible_command ();
4156 AutomationRangeDrag::aborted (bool)
4158 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4159 i->line->clear_always_in_view ();
4164 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4167 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4168 layer = v->region()->layer ();
4169 initial_y = v->get_canvas_group()->property_y ();
4170 initial_playlist = v->region()->playlist ();
4171 initial_position = v->region()->position ();
4172 initial_end = v->region()->position () + v->region()->length ();
4175 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4179 , _cumulative_dx (0)
4181 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4185 PatchChangeDrag::motion (GdkEvent* ev, bool)
4187 framepos_t f = adjusted_current_frame (ev);
4188 boost::shared_ptr<Region> r = _region_view->region ();
4189 f = max (f, r->position ());
4190 f = min (f, r->last_frame ());
4192 framecnt_t const dxf = f - grab_frame();
4193 double const dxu = _editor->frame_to_unit (dxf);
4194 _patch_change->move (dxu - _cumulative_dx, 0);
4195 _cumulative_dx = dxu;
4199 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4201 if (!movement_occurred) {
4205 boost::shared_ptr<Region> r (_region_view->region ());
4207 framepos_t f = adjusted_current_frame (ev);
4208 f = max (f, r->position ());
4209 f = min (f, r->last_frame ());
4211 _region_view->move_patch_change (
4213 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4218 PatchChangeDrag::aborted (bool)
4220 _patch_change->move (-_cumulative_dx, 0);
4224 PatchChangeDrag::setup_pointer_frame_offset ()
4226 boost::shared_ptr<Region> region = _region_view->region ();
4227 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());