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 non-hidden tracks to refer to during the drag */
416 TrackViewList track_views = _editor->track_views;
417 track_views.sort (EditorOrderTimeAxisViewSorter ());
419 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
420 if (!(*i)->hidden()) {
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());
431 /* the list of views can be empty at this point if this is a region list-insert drag
434 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
435 _views.push_back (DraggingView (*i, this));
438 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
442 RegionDrag::region_going_away (RegionView* v)
444 list<DraggingView>::iterator i = _views.begin ();
445 while (i != _views.end() && i->view != v) {
449 if (i != _views.end()) {
454 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
456 RegionDrag::find_time_axis_view (TimeAxisView* t) const
459 int const N = _time_axis_views.size ();
460 while (i < N && _time_axis_views[i] != t) {
471 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
472 : RegionDrag (e, i, p, v),
481 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
483 Drag::start_grab (event, cursor);
485 show_verbose_cursor_time (_last_frame_position);
487 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
488 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
489 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
493 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
495 /* compute the amount of pointer motion in frames, and where
496 the region would be if we moved it by that much.
498 *pending_region_position = adjusted_current_frame (event);
500 framepos_t sync_frame;
501 framecnt_t sync_offset;
504 sync_offset = _primary->region()->sync_offset (sync_dir);
506 /* we don't handle a sync point that lies before zero.
508 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
510 sync_frame = *pending_region_position + (sync_dir*sync_offset);
512 _editor->snap_to_with_modifier (sync_frame, event);
514 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
517 *pending_region_position = _last_frame_position;
520 if (*pending_region_position > max_framepos - _primary->region()->length()) {
521 *pending_region_position = _last_frame_position;
526 /* in locked edit mode, reverse the usual meaning of _x_constrained */
527 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
529 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
531 /* x movement since last time (in pixels) */
532 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
534 /* total x movement */
535 framecnt_t total_dx = *pending_region_position;
536 if (regions_came_from_canvas()) {
537 total_dx = total_dx - grab_frame ();
540 /* check that no regions have gone off the start of the session */
541 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
542 if ((i->view->region()->position() + total_dx) < 0) {
544 *pending_region_position = _last_frame_position;
549 _last_frame_position = *pending_region_position;
556 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
558 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
559 int const n = i->time_axis_view + delta_track;
560 if (n < 0 || n >= int (_time_axis_views.size())) {
561 /* off the top or bottom track */
565 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
566 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
567 /* not a track, or the wrong type */
571 int const l = i->layer + delta_layer;
572 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
573 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
574 If it has, the layers will be munged later anyway, so it's ok.
580 /* all regions being dragged are ok with this change */
585 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
587 assert (!_views.empty ());
589 /* Find the TimeAxisView that the pointer is now over */
590 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
592 /* Bail early if we're not over a track */
593 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
594 if (!rtv || !rtv->is_track()) {
595 _editor->verbose_cursor()->hide ();
599 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
601 /* Here's the current pointer position in terms of time axis view and layer */
602 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
603 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
605 /* Work out the change in x */
606 framepos_t pending_region_position;
607 double const x_delta = compute_x_delta (event, &pending_region_position);
609 /* Work out the change in y */
610 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
611 int delta_layer = current_pointer_layer - _last_pointer_layer;
613 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
614 /* this y movement is not allowed, so do no y movement this time */
615 delta_time_axis_view = 0;
619 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
620 /* haven't reached next snap point, and we're not switching
621 trackviews nor layers. nothing to do.
626 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
628 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
630 RegionView* rv = i->view;
632 if (rv->region()->locked()) {
638 /* here we are calculating the y distance from the
639 top of the first track view to the top of the region
640 area of the track view that we're working on */
642 /* this x value is just a dummy value so that we have something
647 /* distance from the top of this track view to the region area
648 of our track view is always 1 */
652 /* convert to world coordinates, ie distance from the top of
655 rv->get_canvas_frame()->i2w (ix1, iy1);
657 /* compensate for the ruler section and the vertical scrollbar position */
658 iy1 += _editor->get_trackview_group_vertical_offset ();
660 // hide any dependent views
662 rv->get_time_axis_view().hide_dependent_views (*rv);
665 reparent to a non scrolling group so that we can keep the
666 region selection above all time axis views.
667 reparenting means we have to move the rv as the two
668 parent groups have different coordinates.
671 rv->get_canvas_group()->property_y() = iy1 - 1;
672 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
674 rv->fake_set_opaque (true);
677 /* Work out the change in y position of this region view */
681 /* If we have moved tracks, we'll fudge the layer delta so that the
682 region gets moved back onto layer 0 on its new track; this avoids
683 confusion when dragging regions from non-zero layers onto different
686 int this_delta_layer = delta_layer;
687 if (delta_time_axis_view != 0) {
688 this_delta_layer = - i->layer;
691 /* Move this region to layer 0 on its old track */
692 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
693 if (lv->layer_display() == Stacked) {
694 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
697 /* Now move it to its right layer on the current track */
698 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
699 if (cv->layer_display() == Stacked) {
700 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
704 if (delta_time_axis_view > 0) {
705 for (int j = 0; j < delta_time_axis_view; ++j) {
706 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
709 /* start by subtracting the height of the track above where we are now */
710 for (int j = 1; j <= -delta_time_axis_view; ++j) {
711 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
716 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
718 /* Update the DraggingView */
719 i->time_axis_view += delta_time_axis_view;
720 i->layer += this_delta_layer;
723 _editor->mouse_brush_insert_region (rv, pending_region_position);
725 rv->move (x_delta, y_delta);
728 } /* foreach region */
730 _total_x_delta += x_delta;
733 _editor->cursor_group->raise_to_top();
736 if (x_delta != 0 && !_brushing) {
737 show_verbose_cursor_time (_last_frame_position);
740 _last_pointer_time_axis_view += delta_time_axis_view;
741 _last_pointer_layer += delta_layer;
745 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
747 if (_copy && first_move) {
749 /* duplicate the regionview(s) and region(s) */
751 list<DraggingView> new_regionviews;
753 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
755 RegionView* rv = i->view;
756 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
757 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
759 const boost::shared_ptr<const Region> original = rv->region();
760 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
761 region_copy->set_position (original->position());
765 boost::shared_ptr<AudioRegion> audioregion_copy
766 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
768 nrv = new AudioRegionView (*arv, audioregion_copy);
770 boost::shared_ptr<MidiRegion> midiregion_copy
771 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
772 nrv = new MidiRegionView (*mrv, midiregion_copy);
777 nrv->get_canvas_group()->show ();
778 new_regionviews.push_back (DraggingView (nrv, this));
780 /* swap _primary to the copy */
782 if (rv == _primary) {
786 /* ..and deselect the one we copied */
788 rv->set_selected (false);
791 if (!new_regionviews.empty()) {
793 /* reflect the fact that we are dragging the copies */
795 _views = new_regionviews;
797 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
800 sync the canvas to what we think is its current state
801 without it, the canvas seems to
802 "forget" to update properly after the upcoming reparent()
803 ..only if the mouse is in rapid motion at the time of the grab.
804 something to do with regionview creation taking so long?
806 _editor->update_canvas_now();
810 RegionMotionDrag::motion (event, first_move);
814 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
816 if (!movement_occurred) {
821 /* reverse this here so that we have the correct logic to finalize
825 if (Config->get_edit_mode() == Lock) {
826 _x_constrained = !_x_constrained;
829 assert (!_views.empty ());
831 bool const changed_position = (_last_frame_position != _primary->region()->position());
832 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
833 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
835 _editor->update_canvas_now ();
857 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
859 RegionSelection new_views;
860 PlaylistSet modified_playlists;
861 list<RegionView*> views_to_delete;
864 /* all changes were made during motion event handlers */
866 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
870 _editor->commit_reversible_command ();
874 if (_x_constrained) {
875 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
877 _editor->begin_reversible_command (Operations::region_copy);
880 /* insert the regions into their new playlists */
881 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
883 if (i->view->region()->locked()) {
889 if (changed_position && !_x_constrained) {
890 where = i->view->region()->position() - drag_delta;
892 where = i->view->region()->position();
895 RegionView* new_view = insert_region_into_playlist (
896 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
903 new_views.push_back (new_view);
905 /* we don't need the copied RegionView any more */
906 views_to_delete.push_back (i->view);
909 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
910 because when views are deleted they are automagically removed from _views, which messes
913 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
917 /* If we've created new regions either by copying or moving
918 to a new track, we want to replace the old selection with the new ones
921 if (new_views.size() > 0) {
922 _editor->selection->set (new_views);
925 /* write commands for the accumulated diffs for all our modified playlists */
926 add_stateful_diff_commands_for_playlists (modified_playlists);
928 _editor->commit_reversible_command ();
932 RegionMoveDrag::finished_no_copy (
933 bool const changed_position,
934 bool const changed_tracks,
935 framecnt_t const drag_delta
938 RegionSelection new_views;
939 PlaylistSet modified_playlists;
940 PlaylistSet frozen_playlists;
943 /* all changes were made during motion event handlers */
944 _editor->commit_reversible_command ();
948 if (_x_constrained) {
949 _editor->begin_reversible_command (_("fixed time region drag"));
951 _editor->begin_reversible_command (Operations::region_drag);
954 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
956 RegionView* rv = i->view;
958 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
959 layer_t const dest_layer = i->layer;
961 if (rv->region()->locked()) {
968 if (changed_position && !_x_constrained) {
969 where = rv->region()->position() - drag_delta;
971 where = rv->region()->position();
974 if (changed_tracks) {
976 /* insert into new playlist */
978 RegionView* new_view = insert_region_into_playlist (
979 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
987 new_views.push_back (new_view);
989 /* remove from old playlist */
991 /* the region that used to be in the old playlist is not
992 moved to the new one - we use a copy of it. as a result,
993 any existing editor for the region should no longer be
996 rv->hide_region_editor();
997 rv->fake_set_opaque (false);
999 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1003 rv->region()->clear_changes ();
1006 motion on the same track. plonk the previously reparented region
1007 back to its original canvas group (its streamview).
1008 No need to do anything for copies as they are fake regions which will be deleted.
1011 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1012 rv->get_canvas_group()->property_y() = i->initial_y;
1013 rv->get_time_axis_view().reveal_dependent_views (*rv);
1015 /* just change the model */
1017 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1019 if (dest_rtv->view()->layer_display() == Stacked) {
1020 rv->region()->set_layer (dest_layer);
1021 rv->region()->set_pending_explicit_relayer (true);
1024 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1026 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1029 playlist->freeze ();
1032 /* this movement may result in a crossfade being modified, so we need to get undo
1033 data from the playlist as well as the region.
1036 r = modified_playlists.insert (playlist);
1038 playlist->clear_changes ();
1041 rv->region()->set_position (where);
1043 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1046 if (changed_tracks) {
1048 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1049 was selected in all of them, then removing it from a playlist will have removed all
1050 trace of it from _views (i.e. there were N regions selected, we removed 1,
1051 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1052 corresponding regionview, and _views is now empty).
1054 This could have invalidated any and all iterators into _views.
1056 The heuristic we use here is: if the region selection is empty, break out of the loop
1057 here. if the region selection is not empty, then restart the loop because we know that
1058 we must have removed at least the region(view) we've just been working on as well as any
1059 that we processed on previous iterations.
1061 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1062 we can just iterate.
1066 if (_views.empty()) {
1077 /* If we've created new regions either by copying or moving
1078 to a new track, we want to replace the old selection with the new ones
1081 if (new_views.size() > 0) {
1082 _editor->selection->set (new_views);
1085 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1089 /* write commands for the accumulated diffs for all our modified playlists */
1090 add_stateful_diff_commands_for_playlists (modified_playlists);
1092 _editor->commit_reversible_command ();
1095 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1096 * @param region Region to remove.
1097 * @param playlist playlist To remove from.
1098 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1099 * that clear_changes () is only called once per playlist.
1102 RegionMoveDrag::remove_region_from_playlist (
1103 boost::shared_ptr<Region> region,
1104 boost::shared_ptr<Playlist> playlist,
1105 PlaylistSet& modified_playlists
1108 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1111 playlist->clear_changes ();
1114 playlist->remove_region (region);
1118 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1119 * clearing the playlist's diff history first if necessary.
1120 * @param region Region to insert.
1121 * @param dest_rtv Destination RouteTimeAxisView.
1122 * @param dest_layer Destination layer.
1123 * @param where Destination position.
1124 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1125 * that clear_changes () is only called once per playlist.
1126 * @return New RegionView, or 0 if no insert was performed.
1129 RegionMoveDrag::insert_region_into_playlist (
1130 boost::shared_ptr<Region> region,
1131 RouteTimeAxisView* dest_rtv,
1134 PlaylistSet& modified_playlists
1137 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1138 if (!dest_playlist) {
1142 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1143 _new_region_view = 0;
1144 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1146 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1147 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1149 dest_playlist->clear_changes ();
1152 dest_playlist->add_region (region, where);
1154 if (dest_rtv->view()->layer_display() == Stacked) {
1155 region->set_layer (dest_layer);
1156 region->set_pending_explicit_relayer (true);
1161 assert (_new_region_view);
1163 return _new_region_view;
1167 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1169 _new_region_view = rv;
1173 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1175 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1176 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1178 _editor->session()->add_command (c);
1187 RegionMoveDrag::aborted (bool movement_occurred)
1191 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1198 RegionMotionDrag::aborted (movement_occurred);
1203 RegionMotionDrag::aborted (bool)
1205 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1206 RegionView* rv = i->view;
1207 TimeAxisView* tv = &(rv->get_time_axis_view ());
1208 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1210 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1211 rv->get_canvas_group()->property_y() = 0;
1212 rv->get_time_axis_view().reveal_dependent_views (*rv);
1213 rv->fake_set_opaque (false);
1214 rv->move (-_total_x_delta, 0);
1215 rv->set_height (rtv->view()->child_height ());
1218 _editor->update_canvas_now ();
1221 /** @param b true to brush, otherwise false.
1222 * @param c true to make copies of the regions being moved, otherwise false.
1224 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1225 : RegionMotionDrag (e, i, p, v, b),
1228 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1231 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1232 if (rtv && rtv->is_track()) {
1233 speed = rtv->track()->speed ();
1236 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1240 RegionMoveDrag::setup_pointer_frame_offset ()
1242 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1245 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1246 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1248 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1250 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1251 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1253 _primary = v->view()->create_region_view (r, false, false);
1255 _primary->get_canvas_group()->show ();
1256 _primary->set_position (pos, 0);
1257 _views.push_back (DraggingView (_primary, this));
1259 _last_frame_position = pos;
1261 _item = _primary->get_canvas_group ();
1265 RegionInsertDrag::finished (GdkEvent *, bool)
1267 _editor->update_canvas_now ();
1269 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1271 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1272 _primary->get_canvas_group()->property_y() = 0;
1274 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1276 _editor->begin_reversible_command (Operations::insert_region);
1277 playlist->clear_changes ();
1278 playlist->add_region (_primary->region (), _last_frame_position);
1279 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1280 _editor->commit_reversible_command ();
1288 RegionInsertDrag::aborted (bool)
1295 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1296 : RegionMoveDrag (e, i, p, v, false, false)
1298 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1301 struct RegionSelectionByPosition {
1302 bool operator() (RegionView*a, RegionView* b) {
1303 return a->region()->position () < b->region()->position();
1308 RegionSpliceDrag::motion (GdkEvent* event, bool)
1310 /* Which trackview is this ? */
1312 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1313 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1315 /* The region motion is only processed if the pointer is over
1319 if (!tv || !tv->is_track()) {
1320 /* To make sure we hide the verbose canvas cursor when the mouse is
1321 not held over and audiotrack.
1323 _editor->verbose_cursor()->hide ();
1329 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1335 RegionSelection copy (_editor->selection->regions);
1337 RegionSelectionByPosition cmp;
1340 framepos_t const pf = adjusted_current_frame (event);
1342 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1344 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1350 boost::shared_ptr<Playlist> playlist;
1352 if ((playlist = atv->playlist()) == 0) {
1356 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1361 if (pf < (*i)->region()->last_frame() + 1) {
1365 if (pf > (*i)->region()->first_frame()) {
1371 playlist->shuffle ((*i)->region(), dir);
1376 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1378 RegionMoveDrag::finished (event, movement_occurred);
1382 RegionSpliceDrag::aborted (bool)
1387 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1389 _view (dynamic_cast<MidiTimeAxisView*> (v))
1391 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1397 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1401 _view->playlist()->freeze ();
1404 framepos_t const f = adjusted_current_frame (event);
1405 if (f < grab_frame()) {
1406 _region->set_position (f);
1409 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1410 so that if this region is duplicated, its duplicate starts on
1411 a snap point rather than 1 frame after a snap point. Otherwise things get
1412 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1413 place snapped notes at the start of the region.
1416 framecnt_t const len = abs (f - grab_frame () - 1);
1417 _region->set_length (len < 1 ? 1 : len);
1423 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1425 if (!movement_occurred) {
1428 _view->playlist()->thaw ();
1432 _editor->commit_reversible_command ();
1437 RegionCreateDrag::add_region ()
1439 if (_editor->session()) {
1440 const TempoMap& map (_editor->session()->tempo_map());
1441 framecnt_t pos = grab_frame();
1442 const Meter& m = map.meter_at (pos);
1443 /* not that the frame rate used here can be affected by pull up/down which
1446 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1447 _region = _view->add_region (grab_frame(), len, false);
1452 RegionCreateDrag::aborted (bool)
1455 _view->playlist()->thaw ();
1461 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1465 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1469 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1471 Gdk::Cursor* cursor;
1472 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1473 float x_fraction = cnote->mouse_x_fraction ();
1475 if (x_fraction > 0.0 && x_fraction < 0.25) {
1476 cursor = _editor->cursors()->left_side_trim;
1478 cursor = _editor->cursors()->right_side_trim;
1481 Drag::start_grab (event, cursor);
1483 region = &cnote->region_view();
1485 double const region_start = region->get_position_pixels();
1486 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1488 if (grab_x() <= middle_point) {
1489 cursor = _editor->cursors()->left_side_trim;
1492 cursor = _editor->cursors()->right_side_trim;
1496 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1498 if (event->motion.state & Keyboard::PrimaryModifier) {
1504 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1506 if (ms.size() > 1) {
1507 /* has to be relative, may make no sense otherwise */
1511 /* select this note; if it is already selected, preserve the existing selection,
1512 otherwise make this note the only one selected.
1514 region->note_selected (cnote, cnote->selected ());
1516 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1517 MidiRegionSelection::iterator next;
1520 (*r)->begin_resizing (at_front);
1526 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1528 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1529 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1530 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1535 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1537 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1538 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1539 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1544 NoteResizeDrag::aborted (bool)
1549 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1552 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1556 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1562 RegionGainDrag::finished (GdkEvent *, bool)
1568 RegionGainDrag::aborted (bool)
1573 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1574 : RegionDrag (e, i, p, v)
1576 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1580 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1583 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1584 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1586 if (tv && tv->is_track()) {
1587 speed = tv->track()->speed();
1590 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1591 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1592 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1594 framepos_t const pf = adjusted_current_frame (event);
1596 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1597 /* Move the contents of the region around without changing the region bounds */
1598 _operation = ContentsTrim;
1599 Drag::start_grab (event, _editor->cursors()->trimmer);
1601 /* These will get overridden for a point trim.*/
1602 if (pf < (region_start + region_length/2)) {
1603 /* closer to front */
1604 _operation = StartTrim;
1605 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1608 _operation = EndTrim;
1609 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1613 switch (_operation) {
1615 show_verbose_cursor_time (region_start);
1616 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1617 i->view->trim_front_starting ();
1621 show_verbose_cursor_time (region_end);
1624 show_verbose_cursor_time (pf);
1628 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1629 i->view->region()->suspend_property_changes ();
1634 TrimDrag::motion (GdkEvent* event, bool first_move)
1636 RegionView* rv = _primary;
1639 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1640 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1641 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1643 if (tv && tv->is_track()) {
1644 speed = tv->track()->speed();
1647 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1653 switch (_operation) {
1655 trim_type = "Region start trim";
1658 trim_type = "Region end trim";
1661 trim_type = "Region content trim";
1665 _editor->begin_reversible_command (trim_type);
1667 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1668 RegionView* rv = i->view;
1669 rv->fake_set_opaque (false);
1670 rv->enable_display (false);
1671 rv->region()->playlist()->clear_owned_changes ();
1673 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1676 arv->temporarily_hide_envelope ();
1679 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1680 insert_result = _editor->motion_frozen_playlists.insert (pl);
1682 if (insert_result.second) {
1688 bool non_overlap_trim = false;
1690 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1691 non_overlap_trim = true;
1694 switch (_operation) {
1696 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1697 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1702 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1703 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1709 bool swap_direction = false;
1711 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1712 swap_direction = true;
1715 framecnt_t frame_delta = 0;
1717 bool left_direction = false;
1718 if (last_pointer_frame() > adjusted_current_frame(event)) {
1719 left_direction = true;
1722 if (left_direction) {
1723 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1725 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1728 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1729 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1735 switch (_operation) {
1737 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1740 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1743 show_verbose_cursor_time (adjusted_current_frame (event));
1750 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1752 if (movement_occurred) {
1753 motion (event, false);
1755 /* This must happen before the region's StatefulDiffCommand is created, as it may
1756 `correct' (ahem) the region's _start from being negative to being zero. It
1757 needs to be zero in the undo record.
1759 if (_operation == StartTrim) {
1760 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1761 i->view->trim_front_ending ();
1765 if (!_editor->selection->selected (_primary)) {
1766 _primary->thaw_after_trim ();
1769 set<boost::shared_ptr<Playlist> > diffed_playlists;
1771 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1772 i->view->thaw_after_trim ();
1773 i->view->enable_display (true);
1774 i->view->fake_set_opaque (true);
1776 /* Trimming one region may affect others on the playlist, so we need
1777 to get undo Commands from the whole playlist rather than just the
1778 region. Use diffed_playlists to make sure we don't diff a given
1779 playlist more than once.
1781 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1782 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1783 vector<Command*> cmds;
1785 _editor->session()->add_commands (cmds);
1786 diffed_playlists.insert (p);
1790 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1794 _editor->motion_frozen_playlists.clear ();
1795 _editor->commit_reversible_command();
1798 /* no mouse movement */
1799 _editor->point_trim (event, adjusted_current_frame (event));
1802 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1803 if (_operation == StartTrim) {
1804 i->view->trim_front_ending ();
1807 i->view->region()->resume_property_changes ();
1812 TrimDrag::aborted (bool movement_occurred)
1814 /* Our motion method is changing model state, so use the Undo system
1815 to cancel. Perhaps not ideal, as this will leave an Undo point
1816 behind which may be slightly odd from the user's point of view.
1821 if (movement_occurred) {
1825 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1826 i->view->region()->resume_property_changes ();
1831 TrimDrag::setup_pointer_frame_offset ()
1833 list<DraggingView>::iterator i = _views.begin ();
1834 while (i != _views.end() && i->view != _primary) {
1838 if (i == _views.end()) {
1842 switch (_operation) {
1844 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1847 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1854 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1858 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1860 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1865 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1868 // create a dummy marker for visual representation of moving the copy.
1869 // The actual copying is not done before we reach the finish callback.
1871 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1873 MeterMarker* new_marker = new MeterMarker (
1875 *_editor->meter_group,
1876 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1878 *new MeterSection (_marker->meter())
1881 _item = &new_marker->the_item ();
1882 _marker = new_marker;
1886 MetricSection& section (_marker->meter());
1888 if (!section.movable()) {
1894 Drag::start_grab (event, cursor);
1896 show_verbose_cursor_time (adjusted_current_frame(event));
1900 MeterMarkerDrag::setup_pointer_frame_offset ()
1902 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1906 MeterMarkerDrag::motion (GdkEvent* event, bool)
1908 framepos_t const pf = adjusted_current_frame (event);
1910 _marker->set_position (pf);
1912 show_verbose_cursor_time (pf);
1916 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1918 if (!movement_occurred) {
1922 motion (event, false);
1924 Timecode::BBT_Time when;
1926 TempoMap& map (_editor->session()->tempo_map());
1927 map.bbt_time (last_pointer_frame(), when);
1929 if (_copy == true) {
1930 _editor->begin_reversible_command (_("copy meter mark"));
1931 XMLNode &before = map.get_state();
1932 map.add_meter (_marker->meter(), when);
1933 XMLNode &after = map.get_state();
1934 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1935 _editor->commit_reversible_command ();
1937 // delete the dummy marker we used for visual representation of copying.
1938 // a new visual marker will show up automatically.
1941 _editor->begin_reversible_command (_("move meter mark"));
1942 XMLNode &before = map.get_state();
1943 map.move_meter (_marker->meter(), when);
1944 XMLNode &after = map.get_state();
1945 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1946 _editor->commit_reversible_command ();
1951 MeterMarkerDrag::aborted (bool)
1953 _marker->set_position (_marker->meter().frame ());
1956 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1960 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1962 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1967 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1971 // create a dummy marker for visual representation of moving the copy.
1972 // The actual copying is not done before we reach the finish callback.
1974 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1976 TempoMarker* new_marker = new TempoMarker (
1978 *_editor->tempo_group,
1979 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1981 *new TempoSection (_marker->tempo())
1984 _item = &new_marker->the_item ();
1985 _marker = new_marker;
1989 Drag::start_grab (event, cursor);
1991 show_verbose_cursor_time (adjusted_current_frame (event));
1995 TempoMarkerDrag::setup_pointer_frame_offset ()
1997 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2001 TempoMarkerDrag::motion (GdkEvent* event, bool)
2003 framepos_t const pf = adjusted_current_frame (event);
2004 _marker->set_position (pf);
2005 show_verbose_cursor_time (pf);
2009 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2011 if (!movement_occurred) {
2015 motion (event, false);
2017 Timecode::BBT_Time when;
2019 TempoMap& map (_editor->session()->tempo_map());
2020 map.bbt_time (last_pointer_frame(), when);
2022 if (_copy == true) {
2023 _editor->begin_reversible_command (_("copy tempo mark"));
2024 XMLNode &before = map.get_state();
2025 map.add_tempo (_marker->tempo(), when);
2026 XMLNode &after = map.get_state();
2027 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2028 _editor->commit_reversible_command ();
2030 // delete the dummy marker we used for visual representation of copying.
2031 // a new visual marker will show up automatically.
2034 _editor->begin_reversible_command (_("move tempo mark"));
2035 XMLNode &before = map.get_state();
2036 map.move_tempo (_marker->tempo(), when);
2037 XMLNode &after = map.get_state();
2038 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2039 _editor->commit_reversible_command ();
2044 TempoMarkerDrag::aborted (bool)
2046 _marker->set_position (_marker->tempo().frame());
2049 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2053 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2056 /** Do all the things we do when dragging the playhead to make it look as though
2057 * we have located, without actually doing the locate (because that would cause
2058 * the diskstream buffers to be refilled, which is too slow).
2061 CursorDrag::fake_locate (framepos_t t)
2063 _editor->playhead_cursor->set_position (t);
2065 Session* s = _editor->session ();
2066 if (s->timecode_transmission_suspended ()) {
2067 framepos_t const f = _editor->playhead_cursor->current_frame;
2068 s->send_mmc_locate (f);
2069 s->send_full_time_code (f);
2072 show_verbose_cursor_time (t);
2073 _editor->UpdateAllTransportClocks (t);
2077 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2079 Drag::start_grab (event, c);
2081 _grab_zoom = _editor->frames_per_unit;
2083 framepos_t where = _editor->event_frame (event, 0, 0);
2084 _editor->snap_to_with_modifier (where, event);
2086 _editor->_dragging_playhead = true;
2088 Session* s = _editor->session ();
2091 if (_was_rolling && _stop) {
2095 if (s->is_auditioning()) {
2096 s->cancel_audition ();
2099 s->request_suspend_timecode_transmission ();
2100 while (!s->timecode_transmission_suspended ()) {
2101 /* twiddle our thumbs */
2105 fake_locate (where);
2109 CursorDrag::motion (GdkEvent* event, bool)
2111 if (_drags->current_pointer_y() != last_pointer_y()) {
2113 /* zoom when we move the pointer up and down */
2115 /* y range to operate over (pixels) */
2116 double const y_range = 512;
2117 /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */
2118 double const scale_range = 4;
2119 /* dead zone around the grab point in which to do no zooming (pixels) */
2120 double const dead_zone = 100;
2123 double dy = _drags->current_pointer_y() - grab_y();
2125 if (dy < -dead_zone || dy > dead_zone) {
2126 /* we are outside the dead zone; remove it from our calculation */
2133 /* get a number from -1 to 1 as dy ranges from -y_range to y_range */
2134 double udy = max (min (dy / y_range, 1.0), -1.0);
2136 /* and zoom, using playhead focus temporarily */
2137 Editing::ZoomFocus const zf = _editor->get_zoom_focus ();
2138 _editor->set_zoom_focus (Editing::ZoomFocusPlayhead);
2139 _editor->temporal_zoom (_grab_zoom * pow (scale_range, -udy));
2140 _editor->set_zoom_focus (zf);
2144 framepos_t const adjusted_frame = adjusted_current_frame (event);
2145 if (adjusted_frame != last_pointer_frame()) {
2146 fake_locate (adjusted_frame);
2148 _editor->update_canvas_now ();
2154 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2156 _editor->_dragging_playhead = false;
2158 if (!movement_occurred && _stop) {
2162 motion (event, false);
2164 Session* s = _editor->session ();
2166 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2167 _editor->_pending_locate_request = true;
2168 s->request_resume_timecode_transmission ();
2173 CursorDrag::aborted (bool)
2175 if (_editor->_dragging_playhead) {
2176 _editor->session()->request_resume_timecode_transmission ();
2177 _editor->_dragging_playhead = false;
2180 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2183 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2184 : RegionDrag (e, i, p, v)
2186 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2190 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2192 Drag::start_grab (event, cursor);
2194 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2195 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2197 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2199 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2203 FadeInDrag::setup_pointer_frame_offset ()
2205 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2206 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2207 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2211 FadeInDrag::motion (GdkEvent* event, bool)
2213 framecnt_t fade_length;
2215 framepos_t const pos = adjusted_current_frame (event);
2217 boost::shared_ptr<Region> region = _primary->region ();
2219 if (pos < (region->position() + 64)) {
2220 fade_length = 64; // this should be a minimum defined somewhere
2221 } else if (pos > region->last_frame()) {
2222 fade_length = region->length();
2224 fade_length = pos - region->position();
2227 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2229 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2235 tmp->reset_fade_in_shape_width (fade_length);
2236 tmp->show_fade_line((framecnt_t) fade_length);
2239 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2243 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2245 if (!movement_occurred) {
2249 framecnt_t fade_length;
2251 framepos_t const pos = adjusted_current_frame (event);
2253 boost::shared_ptr<Region> region = _primary->region ();
2255 if (pos < (region->position() + 64)) {
2256 fade_length = 64; // this should be a minimum defined somewhere
2257 } else if (pos > region->last_frame()) {
2258 fade_length = region->length();
2260 fade_length = pos - region->position();
2263 _editor->begin_reversible_command (_("change fade in length"));
2265 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2267 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2273 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2274 XMLNode &before = alist->get_state();
2276 tmp->audio_region()->set_fade_in_length (fade_length);
2277 tmp->audio_region()->set_fade_in_active (true);
2278 tmp->hide_fade_line();
2280 XMLNode &after = alist->get_state();
2281 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2284 _editor->commit_reversible_command ();
2288 FadeInDrag::aborted (bool)
2290 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2291 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2297 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2298 tmp->hide_fade_line();
2302 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2303 : RegionDrag (e, i, p, v)
2305 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2309 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2311 Drag::start_grab (event, cursor);
2313 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2314 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2316 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2318 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2322 FadeOutDrag::setup_pointer_frame_offset ()
2324 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2325 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2326 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2330 FadeOutDrag::motion (GdkEvent* event, bool)
2332 framecnt_t fade_length;
2334 framepos_t const pos = adjusted_current_frame (event);
2336 boost::shared_ptr<Region> region = _primary->region ();
2338 if (pos > (region->last_frame() - 64)) {
2339 fade_length = 64; // this should really be a minimum fade defined somewhere
2341 else if (pos < region->position()) {
2342 fade_length = region->length();
2345 fade_length = region->last_frame() - pos;
2348 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2350 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2356 tmp->reset_fade_out_shape_width (fade_length);
2357 tmp->show_fade_line(region->length() - fade_length);
2360 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2364 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2366 if (!movement_occurred) {
2370 framecnt_t fade_length;
2372 framepos_t const pos = adjusted_current_frame (event);
2374 boost::shared_ptr<Region> region = _primary->region ();
2376 if (pos > (region->last_frame() - 64)) {
2377 fade_length = 64; // this should really be a minimum fade defined somewhere
2379 else if (pos < region->position()) {
2380 fade_length = region->length();
2383 fade_length = region->last_frame() - pos;
2386 _editor->begin_reversible_command (_("change fade out length"));
2388 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2390 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2396 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2397 XMLNode &before = alist->get_state();
2399 tmp->audio_region()->set_fade_out_length (fade_length);
2400 tmp->audio_region()->set_fade_out_active (true);
2401 tmp->hide_fade_line();
2403 XMLNode &after = alist->get_state();
2404 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2407 _editor->commit_reversible_command ();
2411 FadeOutDrag::aborted (bool)
2413 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2414 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2420 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2421 tmp->hide_fade_line();
2425 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2428 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2430 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2433 _points.push_back (Gnome::Art::Point (0, 0));
2434 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2437 MarkerDrag::~MarkerDrag ()
2439 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2445 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2447 Drag::start_grab (event, cursor);
2451 Location *location = _editor->find_location_from_marker (_marker, is_start);
2452 _editor->_dragging_edit_point = true;
2454 update_item (location);
2456 // _drag_line->show();
2457 // _line->raise_to_top();
2460 show_verbose_cursor_time (location->start());
2462 show_verbose_cursor_time (location->end());
2465 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2468 case Selection::Toggle:
2469 _editor->selection->toggle (_marker);
2471 case Selection::Set:
2472 if (!_editor->selection->selected (_marker)) {
2473 _editor->selection->set (_marker);
2476 case Selection::Extend:
2478 Locations::LocationList ll;
2479 list<Marker*> to_add;
2481 _editor->selection->markers.range (s, e);
2482 s = min (_marker->position(), s);
2483 e = max (_marker->position(), e);
2486 if (e < max_framepos) {
2489 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2490 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2491 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2494 to_add.push_back (lm->start);
2497 to_add.push_back (lm->end);
2501 if (!to_add.empty()) {
2502 _editor->selection->add (to_add);
2506 case Selection::Add:
2507 _editor->selection->add (_marker);
2511 /* Set up copies for us to manipulate during the drag */
2513 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2514 Location* l = _editor->find_location_from_marker (*i, is_start);
2515 _copied_locations.push_back (new Location (*l));
2520 MarkerDrag::setup_pointer_frame_offset ()
2523 Location *location = _editor->find_location_from_marker (_marker, is_start);
2524 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2528 MarkerDrag::motion (GdkEvent* event, bool)
2530 framecnt_t f_delta = 0;
2532 bool move_both = false;
2534 Location *real_location;
2535 Location *copy_location = 0;
2537 framepos_t const newframe = adjusted_current_frame (event);
2539 framepos_t next = newframe;
2541 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2545 MarkerSelection::iterator i;
2546 list<Location*>::iterator x;
2548 /* find the marker we're dragging, and compute the delta */
2550 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2551 x != _copied_locations.end() && i != _editor->selection->markers.end();
2557 if (marker == _marker) {
2559 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2564 if (real_location->is_mark()) {
2565 f_delta = newframe - copy_location->start();
2569 switch (marker->type()) {
2570 case Marker::SessionStart:
2571 case Marker::RangeStart:
2572 case Marker::LoopStart:
2573 case Marker::PunchIn:
2574 f_delta = newframe - copy_location->start();
2577 case Marker::SessionEnd:
2578 case Marker::RangeEnd:
2579 case Marker::LoopEnd:
2580 case Marker::PunchOut:
2581 f_delta = newframe - copy_location->end();
2584 /* what kind of marker is this ? */
2592 if (i == _editor->selection->markers.end()) {
2593 /* hmm, impossible - we didn't find the dragged marker */
2597 /* now move them all */
2599 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2600 x != _copied_locations.end() && i != _editor->selection->markers.end();
2606 /* call this to find out if its the start or end */
2608 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2612 if (real_location->locked()) {
2616 if (copy_location->is_mark()) {
2620 copy_location->set_start (copy_location->start() + f_delta);
2624 framepos_t new_start = copy_location->start() + f_delta;
2625 framepos_t new_end = copy_location->end() + f_delta;
2627 if (is_start) { // start-of-range marker
2630 copy_location->set_start (new_start);
2631 copy_location->set_end (new_end);
2632 } else if (new_start < copy_location->end()) {
2633 copy_location->set_start (new_start);
2634 } else if (newframe > 0) {
2635 _editor->snap_to (next, 1, true);
2636 copy_location->set_end (next);
2637 copy_location->set_start (newframe);
2640 } else { // end marker
2643 copy_location->set_end (new_end);
2644 copy_location->set_start (new_start);
2645 } else if (new_end > copy_location->start()) {
2646 copy_location->set_end (new_end);
2647 } else if (newframe > 0) {
2648 _editor->snap_to (next, -1, true);
2649 copy_location->set_start (next);
2650 copy_location->set_end (newframe);
2655 update_item (copy_location);
2657 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2660 lm->set_position (copy_location->start(), copy_location->end());
2664 assert (!_copied_locations.empty());
2666 show_verbose_cursor_time (newframe);
2669 _editor->update_canvas_now ();
2674 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2676 if (!movement_occurred) {
2678 /* just a click, do nothing but finish
2679 off the selection process
2682 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2685 case Selection::Set:
2686 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2687 _editor->selection->set (_marker);
2691 case Selection::Toggle:
2692 case Selection::Extend:
2693 case Selection::Add:
2700 _editor->_dragging_edit_point = false;
2702 _editor->begin_reversible_command ( _("move marker") );
2703 XMLNode &before = _editor->session()->locations()->get_state();
2705 MarkerSelection::iterator i;
2706 list<Location*>::iterator x;
2709 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2710 x != _copied_locations.end() && i != _editor->selection->markers.end();
2713 Location * location = _editor->find_location_from_marker (*i, is_start);
2717 if (location->locked()) {
2721 if (location->is_mark()) {
2722 location->set_start ((*x)->start());
2724 location->set ((*x)->start(), (*x)->end());
2729 XMLNode &after = _editor->session()->locations()->get_state();
2730 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2731 _editor->commit_reversible_command ();
2735 MarkerDrag::aborted (bool)
2741 MarkerDrag::update_item (Location*)
2746 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2748 _cumulative_x_drag (0),
2749 _cumulative_y_drag (0)
2751 if (_zero_gain_fraction < 0.0) {
2752 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2755 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2757 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2763 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2765 Drag::start_grab (event, _editor->cursors()->fader);
2767 // start the grab at the center of the control point so
2768 // the point doesn't 'jump' to the mouse after the first drag
2769 _fixed_grab_x = _point->get_x();
2770 _fixed_grab_y = _point->get_y();
2772 float const fraction = 1 - (_point->get_y() / _point->line().height());
2774 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2776 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2777 event->button.x + 10, event->button.y + 10);
2779 _editor->verbose_cursor()->show ();
2783 ControlPointDrag::motion (GdkEvent* event, bool)
2785 double dx = _drags->current_pointer_x() - last_pointer_x();
2786 double dy = _drags->current_pointer_y() - last_pointer_y();
2788 if (event->button.state & Keyboard::SecondaryModifier) {
2793 /* coordinate in pixels relative to the start of the region (for region-based automation)
2794 or track (for track-based automation) */
2795 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2796 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2798 // calculate zero crossing point. back off by .01 to stay on the
2799 // positive side of zero
2800 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2802 // make sure we hit zero when passing through
2803 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2807 if (_x_constrained) {
2810 if (_y_constrained) {
2814 _cumulative_x_drag = cx - _fixed_grab_x;
2815 _cumulative_y_drag = cy - _fixed_grab_y;
2819 cy = min ((double) _point->line().height(), cy);
2821 framepos_t cx_frames = _editor->unit_to_frame (cx);
2823 if (!_x_constrained) {
2824 _editor->snap_to_with_modifier (cx_frames, event);
2827 cx_frames = min (cx_frames, _point->line().maximum_time());
2829 float const fraction = 1.0 - (cy / _point->line().height());
2831 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2833 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2835 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2839 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2841 if (!movement_occurred) {
2845 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2846 _editor->reset_point_selection ();
2850 motion (event, false);
2853 _point->line().end_drag ();
2854 _editor->session()->commit_reversible_command ();
2858 ControlPointDrag::aborted (bool)
2860 _point->line().reset ();
2864 ControlPointDrag::active (Editing::MouseMode m)
2866 if (m == Editing::MouseGain) {
2867 /* always active in mouse gain */
2871 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2872 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2875 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2878 _cumulative_y_drag (0)
2880 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2884 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2886 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2889 _item = &_line->grab_item ();
2891 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2892 origin, and ditto for y.
2895 double cx = event->button.x;
2896 double cy = event->button.y;
2898 _line->parent_group().w2i (cx, cy);
2900 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2905 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2906 /* no adjacent points */
2910 Drag::start_grab (event, _editor->cursors()->fader);
2912 /* store grab start in parent frame */
2917 double fraction = 1.0 - (cy / _line->height());
2919 _line->start_drag_line (before, after, fraction);
2921 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2922 event->button.x + 10, event->button.y + 10);
2924 _editor->verbose_cursor()->show ();
2928 LineDrag::motion (GdkEvent* event, bool)
2930 double dy = _drags->current_pointer_y() - last_pointer_y();
2932 if (event->button.state & Keyboard::SecondaryModifier) {
2936 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2938 _cumulative_y_drag = cy - _fixed_grab_y;
2941 cy = min ((double) _line->height(), cy);
2943 double const fraction = 1.0 - (cy / _line->height());
2947 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2953 /* we are ignoring x position for this drag, so we can just pass in anything */
2954 _line->drag_motion (0, fraction, true, push);
2956 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2960 LineDrag::finished (GdkEvent* event, bool)
2962 motion (event, false);
2964 _editor->session()->commit_reversible_command ();
2968 LineDrag::aborted (bool)
2973 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2976 _cumulative_x_drag (0)
2978 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2982 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2984 Drag::start_grab (event);
2986 _line = reinterpret_cast<Line*> (_item);
2989 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2991 double cx = event->button.x;
2992 double cy = event->button.y;
2994 _item->property_parent().get_value()->w2i(cx, cy);
2996 /* store grab start in parent frame */
2997 _region_view_grab_x = cx;
2999 _before = *(float*) _item->get_data ("position");
3001 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3003 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3007 FeatureLineDrag::motion (GdkEvent*, bool)
3009 double dx = _drags->current_pointer_x() - last_pointer_x();
3011 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3013 _cumulative_x_drag += dx;
3015 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3024 ArdourCanvas::Points points;
3026 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3028 _line->get_bounds(x1, y2, x2, y2);
3030 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3031 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3033 _line->property_points() = points;
3035 float *pos = new float;
3038 _line->set_data ("position", pos);
3044 FeatureLineDrag::finished (GdkEvent*, bool)
3046 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3047 _arv->update_transient(_before, _before);
3051 FeatureLineDrag::aborted (bool)
3056 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3059 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3063 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3065 Drag::start_grab (event);
3066 show_verbose_cursor_time (adjusted_current_frame (event));
3070 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3077 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3079 framepos_t grab = grab_frame ();
3080 if (Config->get_rubberbanding_snaps_to_grid ()) {
3081 _editor->snap_to_with_modifier (grab, event);
3084 /* base start and end on initial click position */
3094 if (_drags->current_pointer_y() < grab_y()) {
3095 y1 = _drags->current_pointer_y();
3098 y2 = _drags->current_pointer_y();
3103 if (start != end || y1 != y2) {
3105 double x1 = _editor->frame_to_pixel (start);
3106 double x2 = _editor->frame_to_pixel (end);
3108 _editor->rubberband_rect->property_x1() = x1;
3109 _editor->rubberband_rect->property_y1() = y1;
3110 _editor->rubberband_rect->property_x2() = x2;
3111 _editor->rubberband_rect->property_y2() = y2;
3113 _editor->rubberband_rect->show();
3114 _editor->rubberband_rect->raise_to_top();
3116 show_verbose_cursor_time (pf);
3121 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3123 if (movement_occurred) {
3125 motion (event, false);
3128 if (_drags->current_pointer_y() < grab_y()) {
3129 y1 = _drags->current_pointer_y();
3132 y2 = _drags->current_pointer_y();
3137 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3139 _editor->begin_reversible_command (_("rubberband selection"));
3141 if (grab_frame() < last_pointer_frame()) {
3142 _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3144 _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3147 _editor->commit_reversible_command ();
3150 if (!getenv("ARDOUR_SAE")) {
3151 _editor->selection->clear_tracks();
3153 _editor->selection->clear_regions();
3154 _editor->selection->clear_points ();
3155 _editor->selection->clear_lines ();
3158 _editor->rubberband_rect->hide();
3162 RubberbandSelectDrag::aborted (bool)
3164 _editor->rubberband_rect->hide ();
3167 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3168 : RegionDrag (e, i, p, v)
3170 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3174 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3176 Drag::start_grab (event, cursor);
3178 show_verbose_cursor_time (adjusted_current_frame (event));
3182 TimeFXDrag::motion (GdkEvent* event, bool)
3184 RegionView* rv = _primary;
3186 framepos_t const pf = adjusted_current_frame (event);
3188 if (pf > rv->region()->position()) {
3189 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3192 show_verbose_cursor_time (pf);
3196 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3198 _primary->get_time_axis_view().hide_timestretch ();
3200 if (!movement_occurred) {
3204 if (last_pointer_frame() < _primary->region()->position()) {
3205 /* backwards drag of the left edge - not usable */
3209 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3211 float percentage = (double) newlen / (double) _primary->region()->length();
3213 #ifndef USE_RUBBERBAND
3214 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3215 if (_primary->region()->data_type() == DataType::AUDIO) {
3216 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3220 // XXX how do timeFX on multiple regions ?
3225 if (_editor->time_stretch (rs, percentage) == -1) {
3226 error << _("An error occurred while executing time stretch operation") << endmsg;
3231 TimeFXDrag::aborted (bool)
3233 _primary->get_time_axis_view().hide_timestretch ();
3236 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3239 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3243 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3245 Drag::start_grab (event);
3249 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3251 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3255 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3257 if (movement_occurred && _editor->session()) {
3258 /* make sure we stop */
3259 _editor->session()->request_transport_speed (0.0);
3264 ScrubDrag::aborted (bool)
3269 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3273 , _original_pointer_time_axis (-1)
3274 , _last_pointer_time_axis (-1)
3276 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3280 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3282 if (_editor->session() == 0) {
3286 Gdk::Cursor* cursor = 0;
3288 switch (_operation) {
3289 case CreateSelection:
3290 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3295 cursor = _editor->cursors()->selector;
3296 Drag::start_grab (event, cursor);
3299 case SelectionStartTrim:
3300 if (_editor->clicked_axisview) {
3301 _editor->clicked_axisview->order_selection_trims (_item, true);
3303 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3306 case SelectionEndTrim:
3307 if (_editor->clicked_axisview) {
3308 _editor->clicked_axisview->order_selection_trims (_item, false);
3310 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3314 Drag::start_grab (event, cursor);
3318 if (_operation == SelectionMove) {
3319 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3321 show_verbose_cursor_time (adjusted_current_frame (event));
3324 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3328 SelectionDrag::setup_pointer_frame_offset ()
3330 switch (_operation) {
3331 case CreateSelection:
3332 _pointer_frame_offset = 0;
3335 case SelectionStartTrim:
3337 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3340 case SelectionEndTrim:
3341 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3347 SelectionDrag::motion (GdkEvent* event, bool first_move)
3349 framepos_t start = 0;
3353 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3354 if (pending_time_axis.first == 0) {
3358 framepos_t const pending_position = adjusted_current_frame (event);
3360 /* only alter selection if things have changed */
3362 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3366 switch (_operation) {
3367 case CreateSelection:
3369 framepos_t grab = grab_frame ();
3372 _editor->snap_to (grab);
3375 if (pending_position < grab_frame()) {
3376 start = pending_position;
3379 end = pending_position;
3383 /* first drag: Either add to the selection
3384 or create a new selection
3390 /* adding to the selection */
3391 _editor->set_selected_track_as_side_effect (Selection::Add);
3392 //_editor->selection->add (_editor->clicked_axisview);
3393 _editor->clicked_selection = _editor->selection->add (start, end);
3398 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3399 //_editor->selection->set (_editor->clicked_axisview);
3400 _editor->set_selected_track_as_side_effect (Selection::Set);
3403 _editor->clicked_selection = _editor->selection->set (start, end);
3407 /* select the track that we're in */
3408 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3409 // _editor->set_selected_track_as_side_effect (Selection::Add);
3410 _editor->selection->add (pending_time_axis.first);
3411 _added_time_axes.push_back (pending_time_axis.first);
3414 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3415 tracks that we selected in the first place.
3418 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3419 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3421 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3422 while (i != _added_time_axes.end()) {
3424 list<TimeAxisView*>::iterator tmp = i;
3427 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3428 _editor->selection->remove (*i);
3429 _added_time_axes.remove (*i);
3438 case SelectionStartTrim:
3440 start = _editor->selection->time[_editor->clicked_selection].start;
3441 end = _editor->selection->time[_editor->clicked_selection].end;
3443 if (pending_position > end) {
3446 start = pending_position;
3450 case SelectionEndTrim:
3452 start = _editor->selection->time[_editor->clicked_selection].start;
3453 end = _editor->selection->time[_editor->clicked_selection].end;
3455 if (pending_position < start) {
3458 end = pending_position;
3465 start = _editor->selection->time[_editor->clicked_selection].start;
3466 end = _editor->selection->time[_editor->clicked_selection].end;
3468 length = end - start;
3470 start = pending_position;
3471 _editor->snap_to (start);
3473 end = start + length;
3478 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3479 _editor->start_canvas_autoscroll (1, 0);
3483 _editor->selection->replace (_editor->clicked_selection, start, end);
3486 if (_operation == SelectionMove) {
3487 show_verbose_cursor_time(start);
3489 show_verbose_cursor_time(pending_position);
3494 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3496 Session* s = _editor->session();
3498 if (movement_occurred) {
3499 motion (event, false);
3500 /* XXX this is not object-oriented programming at all. ick */
3501 if (_editor->selection->time.consolidate()) {
3502 _editor->selection->TimeChanged ();
3505 /* XXX what if its a music time selection? */
3506 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3507 s->request_play_range (&_editor->selection->time, true);
3512 /* just a click, no pointer movement.*/
3514 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3515 _editor->selection->clear_time();
3518 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3519 _editor->selection->set (_editor->clicked_axisview);
3522 if (s && s->get_play_range () && s->transport_rolling()) {
3523 s->request_stop (false, false);
3528 _editor->stop_canvas_autoscroll ();
3532 SelectionDrag::aborted (bool)
3537 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3542 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3544 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3545 physical_screen_height (_editor->get_window()));
3546 _drag_rect->hide ();
3548 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3549 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3553 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3555 if (_editor->session() == 0) {
3559 Gdk::Cursor* cursor = 0;
3561 if (!_editor->temp_location) {
3562 _editor->temp_location = new Location (*_editor->session());
3565 switch (_operation) {
3566 case CreateRangeMarker:
3567 case CreateTransportMarker:
3568 case CreateCDMarker:
3570 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3575 cursor = _editor->cursors()->selector;
3579 Drag::start_grab (event, cursor);
3581 show_verbose_cursor_time (adjusted_current_frame (event));
3585 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3587 framepos_t start = 0;
3589 ArdourCanvas::SimpleRect *crect;
3591 switch (_operation) {
3592 case CreateRangeMarker:
3593 crect = _editor->range_bar_drag_rect;
3595 case CreateTransportMarker:
3596 crect = _editor->transport_bar_drag_rect;
3598 case CreateCDMarker:
3599 crect = _editor->cd_marker_bar_drag_rect;
3602 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3607 framepos_t const pf = adjusted_current_frame (event);
3609 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3610 framepos_t grab = grab_frame ();
3611 _editor->snap_to (grab);
3613 if (pf < grab_frame()) {
3621 /* first drag: Either add to the selection
3622 or create a new selection.
3627 _editor->temp_location->set (start, end);
3631 update_item (_editor->temp_location);
3633 //_drag_rect->raise_to_top();
3638 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3639 _editor->start_canvas_autoscroll (1, 0);
3643 _editor->temp_location->set (start, end);
3645 double x1 = _editor->frame_to_pixel (start);
3646 double x2 = _editor->frame_to_pixel (end);
3647 crect->property_x1() = x1;
3648 crect->property_x2() = x2;
3650 update_item (_editor->temp_location);
3653 show_verbose_cursor_time (pf);
3658 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3660 Location * newloc = 0;
3664 if (movement_occurred) {
3665 motion (event, false);
3668 switch (_operation) {
3669 case CreateRangeMarker:
3670 case CreateCDMarker:
3672 _editor->begin_reversible_command (_("new range marker"));
3673 XMLNode &before = _editor->session()->locations()->get_state();
3674 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3675 if (_operation == CreateCDMarker) {
3676 flags = Location::IsRangeMarker | Location::IsCDMarker;
3677 _editor->cd_marker_bar_drag_rect->hide();
3680 flags = Location::IsRangeMarker;
3681 _editor->range_bar_drag_rect->hide();
3683 newloc = new Location (
3684 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3687 _editor->session()->locations()->add (newloc, true);
3688 XMLNode &after = _editor->session()->locations()->get_state();
3689 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3690 _editor->commit_reversible_command ();
3694 case CreateTransportMarker:
3695 // popup menu to pick loop or punch
3696 _editor->new_transport_marker_context_menu (&event->button, _item);
3700 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3702 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3707 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3709 if (end == max_framepos) {
3710 end = _editor->session()->current_end_frame ();
3713 if (start == max_framepos) {
3714 start = _editor->session()->current_start_frame ();
3717 switch (_editor->mouse_mode) {
3719 /* find the two markers on either side and then make the selection from it */
3720 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3724 /* find the two markers on either side of the click and make the range out of it */
3725 _editor->selection->set (start, end);
3734 _editor->stop_canvas_autoscroll ();
3738 RangeMarkerBarDrag::aborted (bool)
3744 RangeMarkerBarDrag::update_item (Location* location)
3746 double const x1 = _editor->frame_to_pixel (location->start());
3747 double const x2 = _editor->frame_to_pixel (location->end());
3749 _drag_rect->property_x1() = x1;
3750 _drag_rect->property_x2() = x2;
3753 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3757 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3761 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3763 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3764 Drag::start_grab (event, _editor->cursors()->zoom_out);
3767 Drag::start_grab (event, _editor->cursors()->zoom_in);
3771 show_verbose_cursor_time (adjusted_current_frame (event));
3775 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3780 framepos_t const pf = adjusted_current_frame (event);
3782 framepos_t grab = grab_frame ();
3783 _editor->snap_to_with_modifier (grab, event);
3785 /* base start and end on initial click position */
3797 _editor->zoom_rect->show();
3798 _editor->zoom_rect->raise_to_top();
3801 _editor->reposition_zoom_rect(start, end);
3803 show_verbose_cursor_time (pf);
3808 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3810 if (movement_occurred) {
3811 motion (event, false);
3813 if (grab_frame() < last_pointer_frame()) {
3814 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3816 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3819 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3820 _editor->tav_zoom_step (_zoom_out);
3822 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3826 _editor->zoom_rect->hide();
3830 MouseZoomDrag::aborted (bool)
3832 _editor->zoom_rect->hide ();
3835 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3837 , _cumulative_dx (0)
3838 , _cumulative_dy (0)
3840 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3842 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3843 _region = &_primary->region_view ();
3844 _note_height = _region->midi_stream_view()->note_height ();
3848 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3850 Drag::start_grab (event);
3852 if (!(_was_selected = _primary->selected())) {
3854 /* tertiary-click means extend selection - we'll do that on button release,
3855 so don't add it here, because otherwise we make it hard to figure
3856 out the "extend-to" range.
3859 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3862 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3865 _region->note_selected (_primary, true);
3867 _region->unique_select (_primary);
3873 /** @return Current total drag x change in frames */
3875 NoteDrag::total_dx () const
3878 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3880 /* primary note time */
3881 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3883 /* new time of the primary note relative to the region position */
3884 frameoffset_t st = n + dx;
3886 /* prevent the note being dragged earlier than the region's position */
3891 /* snap and return corresponding delta */
3892 return _region->snap_frame_to_frame (st) - n;
3895 /** @return Current total drag y change in note number */
3897 NoteDrag::total_dy () const
3899 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3903 NoteDrag::motion (GdkEvent *, bool)
3905 /* Total change in x and y since the start of the drag */
3906 frameoffset_t const dx = total_dx ();
3907 int8_t const dy = total_dy ();
3909 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3910 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3911 double const tdy = -dy * _note_height - _cumulative_dy;
3914 _cumulative_dx += tdx;
3915 _cumulative_dy += tdy;
3917 int8_t note_delta = total_dy();
3919 _region->move_selection (tdx, tdy, note_delta);
3921 /* the new note value may be the same as the old one, but we
3922 * don't know what that means because the selection may have
3923 * involved more than one note and we might be doing something
3924 * odd with them. so show the note value anyway, always.
3928 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3930 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3931 (int) floor (new_note));
3933 show_verbose_cursor_text (buf);
3938 NoteDrag::finished (GdkEvent* ev, bool moved)
3941 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3943 if (_was_selected) {
3944 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3946 _region->note_deselected (_primary);
3949 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3950 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3952 if (!extend && !add && _region->selection_size() > 1) {
3953 _region->unique_select (_primary);
3954 } else if (extend) {
3955 _region->note_selected (_primary, true, true);
3957 /* it was added during button press */
3962 _region->note_dropped (_primary, total_dx(), total_dy());
3967 NoteDrag::aborted (bool)
3972 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3973 : Drag (editor, item)
3975 , _nothing_to_drag (false)
3977 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3979 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3982 /* get all lines in the automation view */
3983 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3985 /* find those that overlap the ranges being dragged */
3986 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3987 while (i != lines.end ()) {
3988 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3991 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3993 /* check this range against all the AudioRanges that we are using */
3994 list<AudioRange>::const_iterator k = _ranges.begin ();
3995 while (k != _ranges.end()) {
3996 if (k->coverage (r.first, r.second) != OverlapNone) {
4002 /* add it to our list if it overlaps at all */
4003 if (k != _ranges.end()) {
4008 _lines.push_back (n);
4014 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4018 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4020 Drag::start_grab (event, cursor);
4022 /* Get line states before we start changing things */
4023 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4024 i->state = &i->line->get_state ();
4027 if (_ranges.empty()) {
4029 /* No selected time ranges: drag all points */
4030 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4031 uint32_t const N = i->line->npoints ();
4032 for (uint32_t j = 0; j < N; ++j) {
4033 i->points.push_back (i->line->nth (j));
4039 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4041 framecnt_t const half = (i->start + i->end) / 2;
4043 /* find the line that this audio range starts in */
4044 list<Line>::iterator j = _lines.begin();
4045 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4049 if (j != _lines.end()) {
4050 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4052 /* j is the line that this audio range starts in; fade into it;
4053 64 samples length plucked out of thin air.
4056 framepos_t a = i->start + 64;
4061 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4062 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4064 the_list->add (p, the_list->eval (p));
4065 j->line->add_always_in_view (p);
4066 the_list->add (q, the_list->eval (q));
4067 j->line->add_always_in_view (q);
4070 /* same thing for the end */
4073 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4077 if (j != _lines.end()) {
4078 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4080 /* j is the line that this audio range starts in; fade out of it;
4081 64 samples length plucked out of thin air.
4084 framepos_t b = i->end - 64;
4089 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4090 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4092 the_list->add (p, the_list->eval (p));
4093 j->line->add_always_in_view (p);
4094 the_list->add (q, the_list->eval (q));
4095 j->line->add_always_in_view (q);
4099 _nothing_to_drag = true;
4101 /* Find all the points that should be dragged and put them in the relevant
4102 points lists in the Line structs.
4105 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4107 uint32_t const N = i->line->npoints ();
4108 for (uint32_t j = 0; j < N; ++j) {
4110 /* here's a control point on this line */
4111 ControlPoint* p = i->line->nth (j);
4112 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4114 /* see if it's inside a range */
4115 list<AudioRange>::const_iterator k = _ranges.begin ();
4116 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4120 if (k != _ranges.end()) {
4121 /* dragging this point */
4122 _nothing_to_drag = false;
4123 i->points.push_back (p);
4129 if (_nothing_to_drag) {
4133 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4134 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4139 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4141 if (_nothing_to_drag) {
4145 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4146 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4148 /* we are ignoring x position for this drag, so we can just pass in anything */
4149 i->line->drag_motion (0, f, true, false);
4154 AutomationRangeDrag::finished (GdkEvent* event, bool)
4156 if (_nothing_to_drag) {
4160 motion (event, false);
4161 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4162 i->line->end_drag ();
4163 i->line->clear_always_in_view ();
4166 _editor->session()->commit_reversible_command ();
4170 AutomationRangeDrag::aborted (bool)
4172 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4173 i->line->clear_always_in_view ();
4178 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4181 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4182 layer = v->region()->layer ();
4183 initial_y = v->get_canvas_group()->property_y ();
4184 initial_playlist = v->region()->playlist ();
4185 initial_position = v->region()->position ();
4186 initial_end = v->region()->position () + v->region()->length ();
4189 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4193 , _cumulative_dx (0)
4195 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4199 PatchChangeDrag::motion (GdkEvent* ev, bool)
4201 framepos_t f = adjusted_current_frame (ev);
4202 boost::shared_ptr<Region> r = _region_view->region ();
4203 f = max (f, r->position ());
4204 f = min (f, r->last_frame ());
4206 framecnt_t const dxf = f - grab_frame();
4207 double const dxu = _editor->frame_to_unit (dxf);
4208 _patch_change->move (dxu - _cumulative_dx, 0);
4209 _cumulative_dx = dxu;
4213 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4215 if (!movement_occurred) {
4219 boost::shared_ptr<Region> r (_region_view->region ());
4221 framepos_t f = adjusted_current_frame (ev);
4222 f = max (f, r->position ());
4223 f = min (f, r->last_frame ());
4225 _region_view->move_patch_change (
4227 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4232 PatchChangeDrag::aborted (bool)
4234 _patch_change->move (-_cumulative_dx, 0);
4238 PatchChangeDrag::setup_pointer_frame_offset ()
4240 boost::shared_ptr<Region> region = _region_view->region ();
4241 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());