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/session.h"
33 #include "ardour/dB.h"
34 #include "ardour/region_factory.h"
35 #include "ardour/operations.h"
40 #include "audio_region_view.h"
41 #include "midi_region_view.h"
42 #include "ardour_ui.h"
43 #include "gui_thread.h"
44 #include "control_point.h"
46 #include "region_gain_line.h"
47 #include "editor_drag.h"
48 #include "audio_time_axis.h"
49 #include "midi_time_axis.h"
50 #include "canvas-note.h"
51 #include "selection.h"
52 #include "midi_selection.h"
53 #include "automation_time_axis.h"
55 #include "editor_cursors.h"
56 #include "mouse_cursors.h"
59 using namespace ARDOUR;
62 using namespace Gtkmm2ext;
63 using namespace Editing;
64 using namespace ArdourCanvas;
66 using Gtkmm2ext::Keyboard;
68 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
70 DragManager::DragManager (Editor* e)
73 , _current_pointer_frame (0)
78 DragManager::~DragManager ()
83 /** Call abort for each active drag */
89 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
96 _editor->set_follow_playhead (_old_follow_playhead, false);
102 DragManager::add (Drag* d)
104 d->set_manager (this);
105 _drags.push_back (d);
109 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
111 d->set_manager (this);
112 _drags.push_back (d);
117 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
119 /* Prevent follow playhead during the drag to be nice to the user */
120 _old_follow_playhead = _editor->follow_playhead ();
121 _editor->set_follow_playhead (false);
123 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
125 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
126 (*i)->start_grab (e, c);
130 /** Call end_grab for each active drag.
131 * @return true if any drag reported movement having occurred.
134 DragManager::end_grab (GdkEvent* e)
139 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
140 bool const t = (*i)->end_grab (e);
151 _editor->set_follow_playhead (_old_follow_playhead, false);
157 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
161 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
163 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
164 bool const t = (*i)->motion_handler (e, from_autoscroll);
175 DragManager::have_item (ArdourCanvas::Item* i) const
177 list<Drag*>::const_iterator j = _drags.begin ();
178 while (j != _drags.end() && (*j)->item () != i) {
182 return j != _drags.end ();
185 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
188 , _pointer_frame_offset (0)
189 , _move_threshold_passed (false)
190 , _raw_grab_frame (0)
192 , _last_pointer_frame (0)
198 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
204 cursor = _editor->which_grabber_cursor ();
207 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
211 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
214 cursor = _editor->which_grabber_cursor ();
217 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
219 if (Keyboard::is_button2_event (&event->button)) {
220 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
221 _y_constrained = true;
222 _x_constrained = false;
224 _y_constrained = false;
225 _x_constrained = true;
228 _x_constrained = false;
229 _y_constrained = false;
232 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
233 setup_pointer_frame_offset ();
234 _grab_frame = adjusted_frame (_raw_grab_frame, event);
235 _last_pointer_frame = _grab_frame;
236 _last_pointer_x = _grab_x;
237 _last_pointer_y = _grab_y;
239 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
243 if (_editor->session() && _editor->session()->transport_rolling()) {
246 _was_rolling = false;
249 switch (_editor->snap_type()) {
250 case SnapToRegionStart:
251 case SnapToRegionEnd:
252 case SnapToRegionSync:
253 case SnapToRegionBoundary:
254 _editor->build_region_boundary_cache ();
261 /** Call to end a drag `successfully'. Ungrabs item and calls
262 * subclass' finished() method.
264 * @param event GDK event, or 0.
265 * @return true if some movement occurred, otherwise false.
268 Drag::end_grab (GdkEvent* event)
270 _editor->stop_canvas_autoscroll ();
272 _item->ungrab (event ? event->button.time : 0);
274 finished (event, _move_threshold_passed);
276 _editor->hide_verbose_canvas_cursor();
278 return _move_threshold_passed;
282 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
286 if (f > _pointer_frame_offset) {
287 pos = f - _pointer_frame_offset;
291 _editor->snap_to_with_modifier (pos, event);
298 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
300 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
304 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
306 /* check to see if we have moved in any way that matters since the last motion event */
307 if (_move_threshold_passed &&
308 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
309 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
313 pair<framecnt_t, int> const threshold = move_threshold ();
315 bool const old_move_threshold_passed = _move_threshold_passed;
317 if (!from_autoscroll && !_move_threshold_passed) {
319 bool const xp = (::llabs (_drags->current_pointer_frame () - _grab_frame) >= threshold.first);
320 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
322 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
325 if (active (_editor->mouse_mode) && _move_threshold_passed) {
327 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
328 if (!from_autoscroll) {
329 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
332 motion (event, _move_threshold_passed != old_move_threshold_passed);
334 _last_pointer_x = _drags->current_pointer_x ();
335 _last_pointer_y = _drags->current_pointer_y ();
336 _last_pointer_frame = adjusted_current_frame (event);
344 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
352 aborted (_move_threshold_passed);
354 _editor->stop_canvas_autoscroll ();
355 _editor->hide_verbose_canvas_cursor ();
358 struct EditorOrderTimeAxisViewSorter {
359 bool operator() (TimeAxisView* a, TimeAxisView* b) {
360 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
361 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
363 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
367 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
371 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
373 /* Make a list of non-hidden tracks to refer to during the drag */
375 TrackViewList track_views = _editor->track_views;
376 track_views.sort (EditorOrderTimeAxisViewSorter ());
378 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
379 if (!(*i)->hidden()) {
381 _time_axis_views.push_back (*i);
383 TimeAxisView::Children children_list = (*i)->get_child_list ();
384 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
385 _time_axis_views.push_back (j->get());
390 /* the list of views can be empty at this point if this is a region list-insert drag
393 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
394 _views.push_back (DraggingView (*i, this));
397 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
401 RegionDrag::region_going_away (RegionView* v)
403 list<DraggingView>::iterator i = _views.begin ();
404 while (i != _views.end() && i->view != v) {
408 if (i != _views.end()) {
413 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
415 RegionDrag::find_time_axis_view (TimeAxisView* t) const
418 int const N = _time_axis_views.size ();
419 while (i < N && _time_axis_views[i] != t) {
430 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
431 : RegionDrag (e, i, p, v),
440 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
442 Drag::start_grab (event, cursor);
444 _editor->show_verbose_time_cursor (_last_frame_position, 10);
446 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
447 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
448 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
452 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
454 /* compute the amount of pointer motion in frames, and where
455 the region would be if we moved it by that much.
457 *pending_region_position = adjusted_current_frame (event);
459 framepos_t sync_frame;
460 framecnt_t sync_offset;
463 sync_offset = _primary->region()->sync_offset (sync_dir);
465 /* we don't handle a sync point that lies before zero.
467 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
469 sync_frame = *pending_region_position + (sync_dir*sync_offset);
471 _editor->snap_to_with_modifier (sync_frame, event);
473 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
476 *pending_region_position = _last_frame_position;
479 if (*pending_region_position > max_framepos - _primary->region()->length()) {
480 *pending_region_position = _last_frame_position;
485 /* in locked edit mode, reverse the usual meaning of _x_constrained */
486 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
488 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
490 /* x movement since last time */
491 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
493 /* total x movement */
494 framecnt_t total_dx = *pending_region_position;
495 if (regions_came_from_canvas()) {
496 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
499 /* check that no regions have gone off the start of the session */
500 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
501 if ((i->view->region()->position() + total_dx) < 0) {
503 *pending_region_position = _last_frame_position;
508 _last_frame_position = *pending_region_position;
515 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
517 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
518 int const n = i->time_axis_view + delta_track;
519 if (n < 0 || n >= int (_time_axis_views.size())) {
520 /* off the top or bottom track */
524 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
525 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
526 /* not a track, or the wrong type */
530 int const l = i->layer + delta_layer;
531 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
532 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
533 If it has, the layers will be munged later anyway, so it's ok.
539 /* all regions being dragged are ok with this change */
544 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
546 assert (!_views.empty ());
548 /* Find the TimeAxisView that the pointer is now over */
549 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
551 /* Bail early if we're not over a track */
552 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
553 if (!rtv || !rtv->is_track()) {
554 _editor->hide_verbose_canvas_cursor ();
558 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
560 /* Here's the current pointer position in terms of time axis view and layer */
561 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
562 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
564 /* Work out the change in x */
565 framepos_t pending_region_position;
566 double const x_delta = compute_x_delta (event, &pending_region_position);
568 /* Work out the change in y */
569 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
570 int delta_layer = current_pointer_layer - _last_pointer_layer;
572 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
573 /* this y movement is not allowed, so do no y movement this time */
574 delta_time_axis_view = 0;
578 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
579 /* haven't reached next snap point, and we're not switching
580 trackviews nor layers. nothing to do.
585 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
587 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
589 RegionView* rv = i->view;
591 if (rv->region()->locked()) {
597 /* here we are calculating the y distance from the
598 top of the first track view to the top of the region
599 area of the track view that we're working on */
601 /* this x value is just a dummy value so that we have something
606 /* distance from the top of this track view to the region area
607 of our track view is always 1 */
611 /* convert to world coordinates, ie distance from the top of
614 rv->get_canvas_frame()->i2w (ix1, iy1);
616 /* compensate for the ruler section and the vertical scrollbar position */
617 iy1 += _editor->get_trackview_group_vertical_offset ();
619 // hide any dependent views
621 rv->get_time_axis_view().hide_dependent_views (*rv);
624 reparent to a non scrolling group so that we can keep the
625 region selection above all time axis views.
626 reparenting means we have to move the rv as the two
627 parent groups have different coordinates.
630 rv->get_canvas_group()->property_y() = iy1 - 1;
631 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
633 rv->fake_set_opaque (true);
636 /* Work out the change in y position of this region view */
640 /* If we have moved tracks, we'll fudge the layer delta so that the
641 region gets moved back onto layer 0 on its new track; this avoids
642 confusion when dragging regions from non-zero layers onto different
645 int this_delta_layer = delta_layer;
646 if (delta_time_axis_view != 0) {
647 this_delta_layer = - i->layer;
650 /* Move this region to layer 0 on its old track */
651 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
652 if (lv->layer_display() == Stacked) {
653 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
656 /* Now move it to its right layer on the current track */
657 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
658 if (cv->layer_display() == Stacked) {
659 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
663 if (delta_time_axis_view > 0) {
664 for (int j = 0; j < delta_time_axis_view; ++j) {
665 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
668 /* start by subtracting the height of the track above where we are now */
669 for (int j = 1; j <= -delta_time_axis_view; ++j) {
670 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
675 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
677 /* Update the DraggingView */
678 i->time_axis_view += delta_time_axis_view;
679 i->layer += this_delta_layer;
682 _editor->mouse_brush_insert_region (rv, pending_region_position);
684 rv->move (x_delta, y_delta);
687 } /* foreach region */
689 _total_x_delta += x_delta;
692 _editor->cursor_group->raise_to_top();
695 if (x_delta != 0 && !_brushing) {
696 _editor->show_verbose_time_cursor (_last_frame_position, 10);
699 _last_pointer_time_axis_view += delta_time_axis_view;
700 _last_pointer_layer += delta_layer;
704 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
706 if (_copy && first_move) {
708 /* duplicate the regionview(s) and region(s) */
710 list<DraggingView> new_regionviews;
712 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
714 RegionView* rv = i->view;
715 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
716 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
718 const boost::shared_ptr<const Region> original = rv->region();
719 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
720 region_copy->set_position (original->position(), this);
724 boost::shared_ptr<AudioRegion> audioregion_copy
725 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
727 nrv = new AudioRegionView (*arv, audioregion_copy);
729 boost::shared_ptr<MidiRegion> midiregion_copy
730 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
731 nrv = new MidiRegionView (*mrv, midiregion_copy);
736 nrv->get_canvas_group()->show ();
737 new_regionviews.push_back (DraggingView (nrv, this));
739 /* swap _primary to the copy */
741 if (rv == _primary) {
745 /* ..and deselect the one we copied */
747 rv->set_selected (false);
750 if (!new_regionviews.empty()) {
752 /* reflect the fact that we are dragging the copies */
754 _views = new_regionviews;
756 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
759 sync the canvas to what we think is its current state
760 without it, the canvas seems to
761 "forget" to update properly after the upcoming reparent()
762 ..only if the mouse is in rapid motion at the time of the grab.
763 something to do with regionview creation taking so long?
765 _editor->update_canvas_now();
769 RegionMotionDrag::motion (event, first_move);
773 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
775 if (!movement_occurred) {
780 /* reverse this here so that we have the correct logic to finalize
784 if (Config->get_edit_mode() == Lock) {
785 _x_constrained = !_x_constrained;
788 assert (!_views.empty ());
790 bool const changed_position = (_last_frame_position != _primary->region()->position());
791 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
792 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
794 _editor->update_canvas_now ();
816 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
818 RegionSelection new_views;
819 PlaylistSet modified_playlists;
820 list<RegionView*> views_to_delete;
823 /* all changes were made during motion event handlers */
825 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
829 _editor->commit_reversible_command ();
833 if (_x_constrained) {
834 _editor->begin_reversible_command (_("fixed time region copy"));
836 _editor->begin_reversible_command (_("region copy"));
839 /* insert the regions into their new playlists */
840 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
842 if (i->view->region()->locked()) {
848 if (changed_position && !_x_constrained) {
849 where = i->view->region()->position() - drag_delta;
851 where = i->view->region()->position();
854 RegionView* new_view = insert_region_into_playlist (
855 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
862 new_views.push_back (new_view);
864 /* we don't need the copied RegionView any more */
865 views_to_delete.push_back (i->view);
868 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
869 because when views are deleted they are automagically removed from _views, which messes
872 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
876 /* If we've created new regions either by copying or moving
877 to a new track, we want to replace the old selection with the new ones
880 if (new_views.size() > 0) {
881 _editor->selection->set (new_views);
884 /* write commands for the accumulated diffs for all our modified playlists */
885 add_stateful_diff_commands_for_playlists (modified_playlists);
887 _editor->commit_reversible_command ();
891 RegionMoveDrag::finished_no_copy (
892 bool const changed_position,
893 bool const changed_tracks,
894 framecnt_t const drag_delta
897 RegionSelection new_views;
898 PlaylistSet modified_playlists;
899 PlaylistSet frozen_playlists;
902 /* all changes were made during motion event handlers */
903 _editor->commit_reversible_command ();
907 if (_x_constrained) {
908 _editor->begin_reversible_command (_("fixed time region drag"));
910 _editor->begin_reversible_command (Operations::region_drag);
913 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
915 RegionView* rv = i->view;
917 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
918 layer_t const dest_layer = i->layer;
920 if (rv->region()->locked()) {
927 if (changed_position && !_x_constrained) {
928 where = rv->region()->position() - drag_delta;
930 where = rv->region()->position();
933 if (changed_tracks) {
935 /* insert into new playlist */
937 RegionView* new_view = insert_region_into_playlist (
938 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
946 new_views.push_back (new_view);
948 /* remove from old playlist */
950 /* the region that used to be in the old playlist is not
951 moved to the new one - we use a copy of it. as a result,
952 any existing editor for the region should no longer be
955 rv->hide_region_editor();
956 rv->fake_set_opaque (false);
958 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
962 rv->region()->clear_changes ();
965 motion on the same track. plonk the previously reparented region
966 back to its original canvas group (its streamview).
967 No need to do anything for copies as they are fake regions which will be deleted.
970 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
971 rv->get_canvas_group()->property_y() = i->initial_y;
972 rv->get_time_axis_view().reveal_dependent_views (*rv);
974 /* just change the model */
976 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
978 if (dest_rtv->view()->layer_display() == Stacked) {
979 rv->region()->set_layer (dest_layer);
980 rv->region()->set_pending_explicit_relayer (true);
983 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
985 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
991 /* this movement may result in a crossfade being modified, so we need to get undo
992 data from the playlist as well as the region.
995 r = modified_playlists.insert (playlist);
997 playlist->clear_changes ();
1000 rv->region()->set_position (where, (void*) this);
1002 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1005 if (changed_tracks) {
1007 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1008 was selected in all of them, then removing it from a playlist will have removed all
1009 trace of it from _views (i.e. there were N regions selected, we removed 1,
1010 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1011 corresponding regionview, and _views is now empty).
1013 This could have invalidated any and all iterators into _views.
1015 The heuristic we use here is: if the region selection is empty, break out of the loop
1016 here. if the region selection is not empty, then restart the loop because we know that
1017 we must have removed at least the region(view) we've just been working on as well as any
1018 that we processed on previous iterations.
1020 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1021 we can just iterate.
1025 if (_views.empty()) {
1036 /* If we've created new regions either by copying or moving
1037 to a new track, we want to replace the old selection with the new ones
1040 if (new_views.size() > 0) {
1041 _editor->selection->set (new_views);
1044 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1048 /* write commands for the accumulated diffs for all our modified playlists */
1049 add_stateful_diff_commands_for_playlists (modified_playlists);
1051 _editor->commit_reversible_command ();
1054 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1055 * @param region Region to remove.
1056 * @param playlist playlist To remove from.
1057 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1058 * that clear_changes () is only called once per playlist.
1061 RegionMoveDrag::remove_region_from_playlist (
1062 boost::shared_ptr<Region> region,
1063 boost::shared_ptr<Playlist> playlist,
1064 PlaylistSet& modified_playlists
1067 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1070 playlist->clear_changes ();
1073 playlist->remove_region (region);
1077 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1078 * clearing the playlist's diff history first if necessary.
1079 * @param region Region to insert.
1080 * @param dest_rtv Destination RouteTimeAxisView.
1081 * @param dest_layer Destination layer.
1082 * @param where Destination position.
1083 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1084 * that clear_changes () is only called once per playlist.
1085 * @return New RegionView, or 0 if no insert was performed.
1088 RegionMoveDrag::insert_region_into_playlist (
1089 boost::shared_ptr<Region> region,
1090 RouteTimeAxisView* dest_rtv,
1093 PlaylistSet& modified_playlists
1096 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1097 if (!dest_playlist) {
1101 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1102 _new_region_view = 0;
1103 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1105 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1106 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1108 dest_playlist->clear_changes ();
1111 dest_playlist->add_region (region, where);
1113 if (dest_rtv->view()->layer_display() == Stacked) {
1114 region->set_layer (dest_layer);
1115 region->set_pending_explicit_relayer (true);
1120 assert (_new_region_view);
1122 return _new_region_view;
1126 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1128 _new_region_view = rv;
1132 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1134 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1135 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1137 _editor->session()->add_command (c);
1146 RegionMoveDrag::aborted (bool movement_occurred)
1150 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1157 RegionMotionDrag::aborted (movement_occurred);
1162 RegionMotionDrag::aborted (bool)
1164 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1165 RegionView* rv = i->view;
1166 TimeAxisView* tv = &(rv->get_time_axis_view ());
1167 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1169 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1170 rv->get_canvas_group()->property_y() = 0;
1171 rv->get_time_axis_view().reveal_dependent_views (*rv);
1172 rv->fake_set_opaque (false);
1173 rv->move (-_total_x_delta, 0);
1174 rv->set_height (rtv->view()->child_height ());
1177 _editor->update_canvas_now ();
1180 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1181 : RegionMotionDrag (e, i, p, v, b),
1184 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1187 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1188 if (rtv && rtv->is_track()) {
1189 speed = rtv->track()->speed ();
1192 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1196 RegionMoveDrag::setup_pointer_frame_offset ()
1198 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1201 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1202 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1204 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1206 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1207 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1209 _primary = v->view()->create_region_view (r, false, false);
1211 _primary->get_canvas_group()->show ();
1212 _primary->set_position (pos, 0);
1213 _views.push_back (DraggingView (_primary, this));
1215 _last_frame_position = pos;
1217 _item = _primary->get_canvas_group ();
1221 RegionInsertDrag::finished (GdkEvent *, bool)
1223 _editor->update_canvas_now ();
1225 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1227 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1228 _primary->get_canvas_group()->property_y() = 0;
1230 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1232 _editor->begin_reversible_command (Operations::insert_region);
1233 playlist->clear_changes ();
1234 playlist->add_region (_primary->region (), _last_frame_position);
1235 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1236 _editor->commit_reversible_command ();
1244 RegionInsertDrag::aborted (bool)
1251 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1252 : RegionMoveDrag (e, i, p, v, false, false)
1254 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1257 struct RegionSelectionByPosition {
1258 bool operator() (RegionView*a, RegionView* b) {
1259 return a->region()->position () < b->region()->position();
1264 RegionSpliceDrag::motion (GdkEvent* event, bool)
1266 /* Which trackview is this ? */
1268 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1269 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1270 layer_t layer = tvp.second;
1272 if (tv && tv->layer_display() == Overlaid) {
1276 /* The region motion is only processed if the pointer is over
1280 if (!tv || !tv->is_track()) {
1281 /* To make sure we hide the verbose canvas cursor when the mouse is
1282 not held over and audiotrack.
1284 _editor->hide_verbose_canvas_cursor ();
1290 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1296 RegionSelection copy (_editor->selection->regions);
1298 RegionSelectionByPosition cmp;
1301 framepos_t const pf = adjusted_current_frame (event);
1303 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1305 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1311 boost::shared_ptr<Playlist> playlist;
1313 if ((playlist = atv->playlist()) == 0) {
1317 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1322 if (pf < (*i)->region()->last_frame() + 1) {
1326 if (pf > (*i)->region()->first_frame()) {
1332 playlist->shuffle ((*i)->region(), dir);
1337 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1339 RegionMoveDrag::finished (event, movement_occurred);
1343 RegionSpliceDrag::aborted (bool)
1348 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1350 _view (dynamic_cast<MidiTimeAxisView*> (v))
1352 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1358 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1364 framepos_t const f = adjusted_current_frame (event);
1365 if (f < grab_frame()) {
1366 _region->set_position (f, this);
1369 /* again, don't use a zero-length region (see above) */
1370 framecnt_t const len = abs (f - grab_frame ());
1371 _region->set_length (len < 1 ? 1 : len, this);
1377 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1379 if (!movement_occurred) {
1384 _editor->commit_reversible_command ();
1389 RegionCreateDrag::add_region ()
1391 if (_editor->session()) {
1392 const TempoMap& map (_editor->session()->tempo_map());
1393 framecnt_t pos = grab_frame();
1394 const Meter& m = map.meter_at (pos);
1395 /* not that the frame rate used here can be affected by pull up/down which
1398 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1399 _region = _view->add_region (grab_frame(), len, false);
1404 RegionCreateDrag::aborted (bool)
1409 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1413 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1417 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1419 Gdk::Cursor* cursor;
1420 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1421 float x_fraction = cnote->mouse_x_fraction ();
1423 if (x_fraction > 0.0 && x_fraction < 0.25) {
1424 cursor = _editor->cursors()->left_side_trim;
1426 cursor = _editor->cursors()->right_side_trim;
1429 Drag::start_grab (event, cursor);
1431 region = &cnote->region_view();
1433 double const region_start = region->get_position_pixels();
1434 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1436 if (grab_x() <= middle_point) {
1437 cursor = _editor->cursors()->left_side_trim;
1440 cursor = _editor->cursors()->right_side_trim;
1444 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1446 if (event->motion.state & Keyboard::PrimaryModifier) {
1452 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1454 if (ms.size() > 1) {
1455 /* has to be relative, may make no sense otherwise */
1459 /* select this note; if it is already selected, preserve the existing selection,
1460 otherwise make this note the only one selected.
1462 region->note_selected (cnote, cnote->selected ());
1464 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1465 MidiRegionSelection::iterator next;
1468 (*r)->begin_resizing (at_front);
1474 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1476 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1477 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1478 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1483 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1485 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1486 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1487 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1492 NoteResizeDrag::aborted (bool)
1497 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1500 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1504 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1510 RegionGainDrag::finished (GdkEvent *, bool)
1516 RegionGainDrag::aborted (bool)
1521 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1522 : RegionDrag (e, i, p, v)
1524 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1528 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1531 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1532 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1534 if (tv && tv->is_track()) {
1535 speed = tv->track()->speed();
1538 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1539 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1540 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1542 framepos_t const pf = adjusted_current_frame (event);
1544 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1545 /* Move the contents of the region around without changing the region bounds */
1546 _operation = ContentsTrim;
1547 Drag::start_grab (event, _editor->cursors()->trimmer);
1549 /* These will get overridden for a point trim.*/
1550 if (pf < (region_start + region_length/2)) {
1551 /* closer to front */
1552 _operation = StartTrim;
1553 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1556 _operation = EndTrim;
1557 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1561 switch (_operation) {
1563 _editor->show_verbose_time_cursor (region_start, 10);
1564 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1565 i->view->trim_front_starting ();
1569 _editor->show_verbose_time_cursor (region_end, 10);
1572 _editor->show_verbose_time_cursor (pf, 10);
1576 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1577 i->view->region()->suspend_property_changes ();
1582 TrimDrag::motion (GdkEvent* event, bool first_move)
1584 RegionView* rv = _primary;
1587 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1588 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1589 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1591 if (tv && tv->is_track()) {
1592 speed = tv->track()->speed();
1595 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1601 switch (_operation) {
1603 trim_type = "Region start trim";
1606 trim_type = "Region end trim";
1609 trim_type = "Region content trim";
1613 _editor->begin_reversible_command (trim_type);
1615 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1616 RegionView* rv = i->view;
1617 rv->fake_set_opaque (false);
1618 rv->enable_display (false);
1619 rv->region()->playlist()->clear_owned_changes ();
1621 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1624 arv->temporarily_hide_envelope ();
1627 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1628 insert_result = _editor->motion_frozen_playlists.insert (pl);
1630 if (insert_result.second) {
1636 bool non_overlap_trim = false;
1638 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1639 non_overlap_trim = true;
1642 switch (_operation) {
1644 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1645 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1650 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1651 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1657 bool swap_direction = false;
1659 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1660 swap_direction = true;
1663 framecnt_t frame_delta = 0;
1665 bool left_direction = false;
1666 if (last_pointer_frame() > adjusted_current_frame(event)) {
1667 left_direction = true;
1670 if (left_direction) {
1671 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1673 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1676 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1677 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1683 switch (_operation) {
1685 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1688 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1691 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1698 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1700 if (movement_occurred) {
1701 motion (event, false);
1703 /* This must happen before the region's StatefulDiffCommand is created, as it may
1704 `correct' (ahem) the region's _start from being negative to being zero. It
1705 needs to be zero in the undo record.
1707 if (_operation == StartTrim) {
1708 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1709 i->view->trim_front_ending ();
1713 if (!_editor->selection->selected (_primary)) {
1714 _primary->thaw_after_trim ();
1717 set<boost::shared_ptr<Playlist> > diffed_playlists;
1719 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1720 i->view->thaw_after_trim ();
1721 i->view->enable_display (true);
1722 i->view->fake_set_opaque (true);
1724 /* Trimming one region may affect others on the playlist, so we need
1725 to get undo Commands from the whole playlist rather than just the
1726 region. Use diffed_playlists to make sure we don't diff a given
1727 playlist more than once.
1729 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1730 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1731 vector<Command*> cmds;
1733 _editor->session()->add_commands (cmds);
1734 diffed_playlists.insert (p);
1738 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1742 _editor->motion_frozen_playlists.clear ();
1743 _editor->commit_reversible_command();
1746 /* no mouse movement */
1747 _editor->point_trim (event, adjusted_current_frame (event));
1750 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1751 if (_operation == StartTrim) {
1752 i->view->trim_front_ending ();
1755 i->view->region()->resume_property_changes ();
1760 TrimDrag::aborted (bool movement_occurred)
1762 /* Our motion method is changing model state, so use the Undo system
1763 to cancel. Perhaps not ideal, as this will leave an Undo point
1764 behind which may be slightly odd from the user's point of view.
1769 if (movement_occurred) {
1773 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1774 i->view->region()->resume_property_changes ();
1779 TrimDrag::setup_pointer_frame_offset ()
1781 list<DraggingView>::iterator i = _views.begin ();
1782 while (i != _views.end() && i->view != _primary) {
1786 if (i == _views.end()) {
1790 switch (_operation) {
1792 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1795 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1802 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1806 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1808 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1813 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1816 // create a dummy marker for visual representation of moving the copy.
1817 // The actual copying is not done before we reach the finish callback.
1819 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1821 MeterMarker* new_marker = new MeterMarker (
1823 *_editor->meter_group,
1824 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1826 *new MeterSection (_marker->meter())
1829 _item = &new_marker->the_item ();
1830 _marker = new_marker;
1834 MetricSection& section (_marker->meter());
1836 if (!section.movable()) {
1842 Drag::start_grab (event, cursor);
1844 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1848 MeterMarkerDrag::setup_pointer_frame_offset ()
1850 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1854 MeterMarkerDrag::motion (GdkEvent* event, bool)
1856 framepos_t const pf = adjusted_current_frame (event);
1858 _marker->set_position (pf);
1860 _editor->show_verbose_time_cursor (pf, 10);
1864 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1866 if (!movement_occurred) {
1870 motion (event, false);
1872 Timecode::BBT_Time when;
1874 TempoMap& map (_editor->session()->tempo_map());
1875 map.bbt_time (last_pointer_frame(), when);
1877 if (_copy == true) {
1878 _editor->begin_reversible_command (_("copy meter mark"));
1879 XMLNode &before = map.get_state();
1880 map.add_meter (_marker->meter(), when);
1881 XMLNode &after = map.get_state();
1882 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1883 _editor->commit_reversible_command ();
1885 // delete the dummy marker we used for visual representation of copying.
1886 // a new visual marker will show up automatically.
1889 _editor->begin_reversible_command (_("move meter mark"));
1890 XMLNode &before = map.get_state();
1891 map.move_meter (_marker->meter(), when);
1892 XMLNode &after = map.get_state();
1893 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1894 _editor->commit_reversible_command ();
1899 MeterMarkerDrag::aborted (bool)
1901 _marker->set_position (_marker->meter().frame ());
1904 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1908 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1910 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1915 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1919 // create a dummy marker for visual representation of moving the copy.
1920 // The actual copying is not done before we reach the finish callback.
1922 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1924 TempoMarker* new_marker = new TempoMarker (
1926 *_editor->tempo_group,
1927 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1929 *new TempoSection (_marker->tempo())
1932 _item = &new_marker->the_item ();
1933 _marker = new_marker;
1937 Drag::start_grab (event, cursor);
1939 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1943 TempoMarkerDrag::setup_pointer_frame_offset ()
1945 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1949 TempoMarkerDrag::motion (GdkEvent* event, bool)
1951 framepos_t const pf = adjusted_current_frame (event);
1952 _marker->set_position (pf);
1953 _editor->show_verbose_time_cursor (pf, 10);
1957 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1959 if (!movement_occurred) {
1963 motion (event, false);
1965 Timecode::BBT_Time when;
1967 TempoMap& map (_editor->session()->tempo_map());
1968 map.bbt_time (last_pointer_frame(), when);
1970 if (_copy == true) {
1971 _editor->begin_reversible_command (_("copy tempo mark"));
1972 XMLNode &before = map.get_state();
1973 map.add_tempo (_marker->tempo(), when);
1974 XMLNode &after = map.get_state();
1975 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1976 _editor->commit_reversible_command ();
1978 // delete the dummy marker we used for visual representation of copying.
1979 // a new visual marker will show up automatically.
1982 _editor->begin_reversible_command (_("move tempo mark"));
1983 XMLNode &before = map.get_state();
1984 map.move_tempo (_marker->tempo(), when);
1985 XMLNode &after = map.get_state();
1986 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1987 _editor->commit_reversible_command ();
1992 TempoMarkerDrag::aborted (bool)
1994 _marker->set_position (_marker->tempo().frame());
1997 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2001 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2004 /** Do all the things we do when dragging the playhead to make it look as though
2005 * we have located, without actually doing the locate (because that would cause
2006 * the diskstream buffers to be refilled, which is too slow).
2009 CursorDrag::fake_locate (framepos_t t)
2011 _editor->playhead_cursor->set_position (t);
2013 Session* s = _editor->session ();
2014 if (s->timecode_transmission_suspended ()) {
2015 framepos_t const f = _editor->playhead_cursor->current_frame;
2016 s->send_mmc_locate (f);
2017 s->send_full_time_code (f);
2020 _editor->show_verbose_time_cursor (t, 10);
2021 _editor->UpdateAllTransportClocks (t);
2025 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2027 Drag::start_grab (event, c);
2029 framepos_t where = _editor->event_frame (event, 0, 0);
2030 _editor->snap_to_with_modifier (where, event);
2032 _editor->_dragging_playhead = true;
2034 Session* s = _editor->session ();
2037 if (_was_rolling && _stop) {
2041 if (s->is_auditioning()) {
2042 s->cancel_audition ();
2045 s->request_suspend_timecode_transmission ();
2046 while (!s->timecode_transmission_suspended ()) {
2047 /* twiddle our thumbs */
2051 fake_locate (where);
2055 CursorDrag::motion (GdkEvent* event, bool)
2057 framepos_t const adjusted_frame = adjusted_current_frame (event);
2059 if (adjusted_frame == last_pointer_frame()) {
2063 fake_locate (adjusted_frame);
2066 _editor->update_canvas_now ();
2071 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2073 _editor->_dragging_playhead = false;
2075 if (!movement_occurred && _stop) {
2079 motion (event, false);
2081 Session* s = _editor->session ();
2083 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2084 _editor->_pending_locate_request = true;
2085 s->request_resume_timecode_transmission ();
2090 CursorDrag::aborted (bool)
2092 if (_editor->_dragging_playhead) {
2093 _editor->session()->request_resume_timecode_transmission ();
2094 _editor->_dragging_playhead = false;
2097 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2100 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2101 : RegionDrag (e, i, p, v)
2103 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2107 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2109 Drag::start_grab (event, cursor);
2111 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2112 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2114 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2116 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2120 FadeInDrag::setup_pointer_frame_offset ()
2122 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2123 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2124 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2128 FadeInDrag::motion (GdkEvent* event, bool)
2130 framecnt_t fade_length;
2132 framepos_t const pos = adjusted_current_frame (event);
2134 boost::shared_ptr<Region> region = _primary->region ();
2136 if (pos < (region->position() + 64)) {
2137 fade_length = 64; // this should be a minimum defined somewhere
2138 } else if (pos > region->last_frame()) {
2139 fade_length = region->length();
2141 fade_length = pos - region->position();
2144 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2146 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2152 tmp->reset_fade_in_shape_width (fade_length);
2153 tmp->show_fade_line((framecnt_t) fade_length);
2156 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2160 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2162 if (!movement_occurred) {
2166 framecnt_t fade_length;
2168 framepos_t const pos = adjusted_current_frame (event);
2170 boost::shared_ptr<Region> region = _primary->region ();
2172 if (pos < (region->position() + 64)) {
2173 fade_length = 64; // this should be a minimum defined somewhere
2174 } else if (pos > region->last_frame()) {
2175 fade_length = region->length();
2177 fade_length = pos - region->position();
2180 _editor->begin_reversible_command (_("change fade in length"));
2182 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2184 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2190 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2191 XMLNode &before = alist->get_state();
2193 tmp->audio_region()->set_fade_in_length (fade_length);
2194 tmp->audio_region()->set_fade_in_active (true);
2195 tmp->hide_fade_line();
2197 XMLNode &after = alist->get_state();
2198 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2201 _editor->commit_reversible_command ();
2205 FadeInDrag::aborted (bool)
2207 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2208 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2214 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2215 tmp->hide_fade_line();
2219 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2220 : RegionDrag (e, i, p, v)
2222 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2226 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2228 Drag::start_grab (event, cursor);
2230 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2231 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2233 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2235 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2239 FadeOutDrag::setup_pointer_frame_offset ()
2241 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2242 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2243 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2247 FadeOutDrag::motion (GdkEvent* event, bool)
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->last_frame() - 64)) {
2256 fade_length = 64; // this should really be a minimum fade defined somewhere
2258 else if (pos < region->position()) {
2259 fade_length = region->length();
2262 fade_length = region->last_frame() - pos;
2265 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2267 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2273 tmp->reset_fade_out_shape_width (fade_length);
2274 tmp->show_fade_line(region->length() - fade_length);
2277 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2281 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2283 if (!movement_occurred) {
2287 framecnt_t fade_length;
2289 framepos_t const pos = adjusted_current_frame (event);
2291 boost::shared_ptr<Region> region = _primary->region ();
2293 if (pos > (region->last_frame() - 64)) {
2294 fade_length = 64; // this should really be a minimum fade defined somewhere
2296 else if (pos < region->position()) {
2297 fade_length = region->length();
2300 fade_length = region->last_frame() - pos;
2303 _editor->begin_reversible_command (_("change fade out length"));
2305 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2307 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2313 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2314 XMLNode &before = alist->get_state();
2316 tmp->audio_region()->set_fade_out_length (fade_length);
2317 tmp->audio_region()->set_fade_out_active (true);
2318 tmp->hide_fade_line();
2320 XMLNode &after = alist->get_state();
2321 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2324 _editor->commit_reversible_command ();
2328 FadeOutDrag::aborted (bool)
2330 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2331 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2337 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2338 tmp->hide_fade_line();
2342 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2345 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2347 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2350 _points.push_back (Gnome::Art::Point (0, 0));
2351 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2354 MarkerDrag::~MarkerDrag ()
2356 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2362 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2364 Drag::start_grab (event, cursor);
2368 Location *location = _editor->find_location_from_marker (_marker, is_start);
2369 _editor->_dragging_edit_point = true;
2371 update_item (location);
2373 // _drag_line->show();
2374 // _line->raise_to_top();
2377 _editor->show_verbose_time_cursor (location->start(), 10);
2379 _editor->show_verbose_time_cursor (location->end(), 10);
2382 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2385 case Selection::Toggle:
2386 _editor->selection->toggle (_marker);
2388 case Selection::Set:
2389 if (!_editor->selection->selected (_marker)) {
2390 _editor->selection->set (_marker);
2393 case Selection::Extend:
2395 Locations::LocationList ll;
2396 list<Marker*> to_add;
2398 _editor->selection->markers.range (s, e);
2399 s = min (_marker->position(), s);
2400 e = max (_marker->position(), e);
2403 if (e < max_framepos) {
2406 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2407 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2408 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2411 to_add.push_back (lm->start);
2414 to_add.push_back (lm->end);
2418 if (!to_add.empty()) {
2419 _editor->selection->add (to_add);
2423 case Selection::Add:
2424 _editor->selection->add (_marker);
2428 /* Set up copies for us to manipulate during the drag */
2430 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2431 Location* l = _editor->find_location_from_marker (*i, is_start);
2432 _copied_locations.push_back (new Location (*l));
2437 MarkerDrag::setup_pointer_frame_offset ()
2440 Location *location = _editor->find_location_from_marker (_marker, is_start);
2441 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2445 MarkerDrag::motion (GdkEvent* event, bool)
2447 framecnt_t f_delta = 0;
2449 bool move_both = false;
2451 Location *real_location;
2452 Location *copy_location = 0;
2454 framepos_t const newframe = adjusted_current_frame (event);
2456 framepos_t next = newframe;
2458 if (newframe == last_pointer_frame()) {
2462 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2466 MarkerSelection::iterator i;
2467 list<Location*>::iterator x;
2469 /* find the marker we're dragging, and compute the delta */
2471 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2472 x != _copied_locations.end() && i != _editor->selection->markers.end();
2478 if (marker == _marker) {
2480 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2485 if (real_location->is_mark()) {
2486 f_delta = newframe - copy_location->start();
2490 switch (marker->type()) {
2491 case Marker::SessionStart:
2492 case Marker::RangeStart:
2493 case Marker::LoopStart:
2494 case Marker::PunchIn:
2495 f_delta = newframe - copy_location->start();
2498 case Marker::SessionEnd:
2499 case Marker::RangeEnd:
2500 case Marker::LoopEnd:
2501 case Marker::PunchOut:
2502 f_delta = newframe - copy_location->end();
2505 /* what kind of marker is this ? */
2513 if (i == _editor->selection->markers.end()) {
2514 /* hmm, impossible - we didn't find the dragged marker */
2518 /* now move them all */
2520 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2521 x != _copied_locations.end() && i != _editor->selection->markers.end();
2527 /* call this to find out if its the start or end */
2529 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2533 if (real_location->locked()) {
2537 if (copy_location->is_mark()) {
2541 copy_location->set_start (copy_location->start() + f_delta);
2545 framepos_t new_start = copy_location->start() + f_delta;
2546 framepos_t new_end = copy_location->end() + f_delta;
2548 if (is_start) { // start-of-range marker
2551 copy_location->set_start (new_start);
2552 copy_location->set_end (new_end);
2553 } else if (new_start < copy_location->end()) {
2554 copy_location->set_start (new_start);
2555 } else if (newframe > 0) {
2556 _editor->snap_to (next, 1, true);
2557 copy_location->set_end (next);
2558 copy_location->set_start (newframe);
2561 } else { // end marker
2564 copy_location->set_end (new_end);
2565 copy_location->set_start (new_start);
2566 } else if (new_end > copy_location->start()) {
2567 copy_location->set_end (new_end);
2568 } else if (newframe > 0) {
2569 _editor->snap_to (next, -1, true);
2570 copy_location->set_start (next);
2571 copy_location->set_end (newframe);
2576 update_item (copy_location);
2578 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2581 lm->set_position (copy_location->start(), copy_location->end());
2585 assert (!_copied_locations.empty());
2587 _editor->show_verbose_time_cursor (newframe, 10);
2590 _editor->update_canvas_now ();
2595 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2597 if (!movement_occurred) {
2599 /* just a click, do nothing but finish
2600 off the selection process
2603 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2606 case Selection::Set:
2607 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2608 _editor->selection->set (_marker);
2612 case Selection::Toggle:
2613 case Selection::Extend:
2614 case Selection::Add:
2621 _editor->_dragging_edit_point = false;
2623 _editor->begin_reversible_command ( _("move marker") );
2624 XMLNode &before = _editor->session()->locations()->get_state();
2626 MarkerSelection::iterator i;
2627 list<Location*>::iterator x;
2630 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2631 x != _copied_locations.end() && i != _editor->selection->markers.end();
2634 Location * location = _editor->find_location_from_marker (*i, is_start);
2638 if (location->locked()) {
2642 if (location->is_mark()) {
2643 location->set_start ((*x)->start());
2645 location->set ((*x)->start(), (*x)->end());
2650 XMLNode &after = _editor->session()->locations()->get_state();
2651 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2652 _editor->commit_reversible_command ();
2656 MarkerDrag::aborted (bool)
2662 MarkerDrag::update_item (Location* location)
2667 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2669 _cumulative_x_drag (0),
2670 _cumulative_y_drag (0)
2672 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2674 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2680 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2682 Drag::start_grab (event, _editor->cursors()->fader);
2684 // start the grab at the center of the control point so
2685 // the point doesn't 'jump' to the mouse after the first drag
2686 _fixed_grab_x = _point->get_x();
2687 _fixed_grab_y = _point->get_y();
2689 float const fraction = 1 - (_point->get_y() / _point->line().height());
2691 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2693 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2694 event->button.x + 10, event->button.y + 10);
2696 _editor->show_verbose_canvas_cursor ();
2700 ControlPointDrag::motion (GdkEvent* event, bool)
2702 double dx = _drags->current_pointer_x() - last_pointer_x();
2703 double dy = _drags->current_pointer_y() - last_pointer_y();
2705 if (event->button.state & Keyboard::SecondaryModifier) {
2710 /* coordinate in pixels relative to the start of the region (for region-based automation)
2711 or track (for track-based automation) */
2712 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2713 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2715 // calculate zero crossing point. back off by .01 to stay on the
2716 // positive side of zero
2717 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2719 // make sure we hit zero when passing through
2720 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2724 if (_x_constrained) {
2727 if (_y_constrained) {
2731 _cumulative_x_drag = cx - _fixed_grab_x;
2732 _cumulative_y_drag = cy - _fixed_grab_y;
2736 cy = min ((double) _point->line().height(), cy);
2738 framepos_t cx_frames = _editor->unit_to_frame (cx);
2740 if (!_x_constrained) {
2741 _editor->snap_to_with_modifier (cx_frames, event);
2744 cx_frames = min (cx_frames, _point->line().maximum_time());
2746 float const fraction = 1.0 - (cy / _point->line().height());
2748 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2750 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2752 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2756 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2758 if (!movement_occurred) {
2762 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2763 _editor->reset_point_selection ();
2767 motion (event, false);
2770 _point->line().end_drag ();
2771 _editor->session()->commit_reversible_command ();
2775 ControlPointDrag::aborted (bool)
2777 _point->line().reset ();
2781 ControlPointDrag::active (Editing::MouseMode m)
2783 if (m == Editing::MouseGain) {
2784 /* always active in mouse gain */
2788 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2789 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2792 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2795 _cumulative_y_drag (0)
2797 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2801 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2803 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2806 _item = &_line->grab_item ();
2808 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2809 origin, and ditto for y.
2812 double cx = event->button.x;
2813 double cy = event->button.y;
2815 _line->parent_group().w2i (cx, cy);
2817 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2822 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2823 /* no adjacent points */
2827 Drag::start_grab (event, _editor->cursors()->fader);
2829 /* store grab start in parent frame */
2834 double fraction = 1.0 - (cy / _line->height());
2836 _line->start_drag_line (before, after, fraction);
2838 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2839 event->button.x + 10, event->button.y + 10);
2841 _editor->show_verbose_canvas_cursor ();
2845 LineDrag::motion (GdkEvent* event, bool)
2847 double dy = _drags->current_pointer_y() - last_pointer_y();
2849 if (event->button.state & Keyboard::SecondaryModifier) {
2853 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2855 _cumulative_y_drag = cy - _fixed_grab_y;
2858 cy = min ((double) _line->height(), cy);
2860 double const fraction = 1.0 - (cy / _line->height());
2864 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2870 /* we are ignoring x position for this drag, so we can just pass in anything */
2871 _line->drag_motion (0, fraction, true, push);
2873 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2877 LineDrag::finished (GdkEvent* event, bool)
2879 motion (event, false);
2881 _editor->session()->commit_reversible_command ();
2885 LineDrag::aborted (bool)
2890 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2893 _cumulative_x_drag (0)
2895 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2899 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2901 Drag::start_grab (event);
2903 _line = reinterpret_cast<SimpleLine*> (_item);
2906 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2908 double cx = event->button.x;
2909 double cy = event->button.y;
2911 _item->property_parent().get_value()->w2i(cx, cy);
2913 /* store grab start in parent frame */
2914 _region_view_grab_x = cx;
2916 _before = _line->property_x1();
2918 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2920 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2924 FeatureLineDrag::motion (GdkEvent*, bool)
2926 double dx = _drags->current_pointer_x() - last_pointer_x();
2928 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2930 _cumulative_x_drag += dx;
2932 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2941 _line->property_x1() = cx;
2942 _line->property_x2() = cx;
2944 _before = _line->property_x1();
2948 FeatureLineDrag::finished (GdkEvent*, bool)
2950 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2951 _arv->update_transient(_before, _line->property_x1());
2955 FeatureLineDrag::aborted (bool)
2960 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2963 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2967 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2969 Drag::start_grab (event);
2970 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2974 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2981 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2983 framepos_t grab = grab_frame ();
2984 if (Config->get_rubberbanding_snaps_to_grid ()) {
2985 _editor->snap_to_with_modifier (grab, event);
2988 /* base start and end on initial click position */
2998 if (_drags->current_pointer_y() < grab_y()) {
2999 y1 = _drags->current_pointer_y();
3002 y2 = _drags->current_pointer_y();
3007 if (start != end || y1 != y2) {
3009 double x1 = _editor->frame_to_pixel (start);
3010 double x2 = _editor->frame_to_pixel (end);
3012 _editor->rubberband_rect->property_x1() = x1;
3013 _editor->rubberband_rect->property_y1() = y1;
3014 _editor->rubberband_rect->property_x2() = x2;
3015 _editor->rubberband_rect->property_y2() = y2;
3017 _editor->rubberband_rect->show();
3018 _editor->rubberband_rect->raise_to_top();
3020 _editor->show_verbose_time_cursor (pf, 10);
3025 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3027 if (movement_occurred) {
3029 motion (event, false);
3032 if (_drags->current_pointer_y() < grab_y()) {
3033 y1 = _drags->current_pointer_y();
3036 y2 = _drags->current_pointer_y();
3041 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3043 _editor->begin_reversible_command (_("rubberband selection"));
3045 if (grab_frame() < last_pointer_frame()) {
3046 _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3048 _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3051 _editor->commit_reversible_command ();
3053 if (!getenv("ARDOUR_SAE")) {
3054 _editor->selection->clear_tracks();
3056 _editor->selection->clear_regions();
3057 _editor->selection->clear_points ();
3058 _editor->selection->clear_lines ();
3061 _editor->rubberband_rect->hide();
3065 RubberbandSelectDrag::aborted (bool)
3067 _editor->rubberband_rect->hide ();
3070 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3071 : RegionDrag (e, i, p, v)
3073 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3077 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3079 Drag::start_grab (event, cursor);
3081 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3085 TimeFXDrag::motion (GdkEvent* event, bool)
3087 RegionView* rv = _primary;
3089 framepos_t const pf = adjusted_current_frame (event);
3091 if (pf > rv->region()->position()) {
3092 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3095 _editor->show_verbose_time_cursor (pf, 10);
3099 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3101 _primary->get_time_axis_view().hide_timestretch ();
3103 if (!movement_occurred) {
3107 if (last_pointer_frame() < _primary->region()->position()) {
3108 /* backwards drag of the left edge - not usable */
3112 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3114 float percentage = (double) newlen / (double) _primary->region()->length();
3116 #ifndef USE_RUBBERBAND
3117 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3118 if (_primary->region()->data_type() == DataType::AUDIO) {
3119 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3123 _editor->begin_reversible_command (_("timestretch"));
3125 // XXX how do timeFX on multiple regions ?
3130 if (_editor->time_stretch (rs, percentage) == -1) {
3131 error << _("An error occurred while executing time stretch operation") << endmsg;
3136 TimeFXDrag::aborted (bool)
3138 _primary->get_time_axis_view().hide_timestretch ();
3141 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3144 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3148 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3150 Drag::start_grab (event);
3154 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3156 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3160 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3162 if (movement_occurred && _editor->session()) {
3163 /* make sure we stop */
3164 _editor->session()->request_transport_speed (0.0);
3169 ScrubDrag::aborted (bool)
3174 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3178 , _original_pointer_time_axis (-1)
3179 , _last_pointer_time_axis (-1)
3181 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3185 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3187 if (_editor->session() == 0) {
3191 Gdk::Cursor* cursor = 0;
3193 switch (_operation) {
3194 case CreateSelection:
3195 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3200 cursor = _editor->cursors()->selector;
3201 Drag::start_grab (event, cursor);
3204 case SelectionStartTrim:
3205 if (_editor->clicked_axisview) {
3206 _editor->clicked_axisview->order_selection_trims (_item, true);
3208 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3211 case SelectionEndTrim:
3212 if (_editor->clicked_axisview) {
3213 _editor->clicked_axisview->order_selection_trims (_item, false);
3215 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3219 Drag::start_grab (event, cursor);
3223 if (_operation == SelectionMove) {
3224 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3226 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3229 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3233 SelectionDrag::setup_pointer_frame_offset ()
3235 switch (_operation) {
3236 case CreateSelection:
3237 _pointer_frame_offset = 0;
3240 case SelectionStartTrim:
3242 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3245 case SelectionEndTrim:
3246 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3252 SelectionDrag::motion (GdkEvent* event, bool first_move)
3254 framepos_t start = 0;
3258 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3259 if (pending_time_axis.first == 0) {
3263 framepos_t const pending_position = adjusted_current_frame (event);
3265 /* only alter selection if things have changed */
3267 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3271 switch (_operation) {
3272 case CreateSelection:
3274 framepos_t grab = grab_frame ();
3277 _editor->snap_to (grab);
3280 if (pending_position < grab_frame()) {
3281 start = pending_position;
3284 end = pending_position;
3288 /* first drag: Either add to the selection
3289 or create a new selection
3295 /* adding to the selection */
3296 _editor->set_selected_track_as_side_effect (Selection::Add);
3297 //_editor->selection->add (_editor->clicked_axisview);
3298 _editor->clicked_selection = _editor->selection->add (start, end);
3303 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3304 //_editor->selection->set (_editor->clicked_axisview);
3305 _editor->set_selected_track_as_side_effect (Selection::Set);
3308 _editor->clicked_selection = _editor->selection->set (start, end);
3312 /* select the track that we're in */
3313 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3314 // _editor->set_selected_track_as_side_effect (Selection::Add);
3315 _editor->selection->add (pending_time_axis.first);
3316 _added_time_axes.push_back (pending_time_axis.first);
3319 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3320 tracks that we selected in the first place.
3323 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3324 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3326 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3327 while (i != _added_time_axes.end()) {
3329 list<TimeAxisView*>::iterator tmp = i;
3332 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3333 _editor->selection->remove (*i);
3334 _added_time_axes.remove (*i);
3343 case SelectionStartTrim:
3345 start = _editor->selection->time[_editor->clicked_selection].start;
3346 end = _editor->selection->time[_editor->clicked_selection].end;
3348 if (pending_position > end) {
3351 start = pending_position;
3355 case SelectionEndTrim:
3357 start = _editor->selection->time[_editor->clicked_selection].start;
3358 end = _editor->selection->time[_editor->clicked_selection].end;
3360 if (pending_position < start) {
3363 end = pending_position;
3370 start = _editor->selection->time[_editor->clicked_selection].start;
3371 end = _editor->selection->time[_editor->clicked_selection].end;
3373 length = end - start;
3375 start = pending_position;
3376 _editor->snap_to (start);
3378 end = start + length;
3383 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3384 _editor->start_canvas_autoscroll (1, 0);
3388 _editor->selection->replace (_editor->clicked_selection, start, end);
3391 if (_operation == SelectionMove) {
3392 _editor->show_verbose_time_cursor(start, 10);
3394 _editor->show_verbose_time_cursor(pending_position, 10);
3399 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3401 Session* s = _editor->session();
3403 if (movement_occurred) {
3404 motion (event, false);
3405 /* XXX this is not object-oriented programming at all. ick */
3406 if (_editor->selection->time.consolidate()) {
3407 _editor->selection->TimeChanged ();
3410 /* XXX what if its a music time selection? */
3411 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3412 s->request_play_range (&_editor->selection->time, true);
3417 /* just a click, no pointer movement.*/
3419 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3420 _editor->selection->clear_time();
3423 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3424 _editor->selection->set (_editor->clicked_axisview);
3427 if (s && s->get_play_range () && s->transport_rolling()) {
3428 s->request_stop (false, false);
3433 _editor->stop_canvas_autoscroll ();
3437 SelectionDrag::aborted (bool)
3442 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3447 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3449 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3450 physical_screen_height (_editor->get_window()));
3451 _drag_rect->hide ();
3453 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3454 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3458 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3460 if (_editor->session() == 0) {
3464 Gdk::Cursor* cursor = 0;
3466 if (!_editor->temp_location) {
3467 _editor->temp_location = new Location (*_editor->session());
3470 switch (_operation) {
3471 case CreateRangeMarker:
3472 case CreateTransportMarker:
3473 case CreateCDMarker:
3475 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3480 cursor = _editor->cursors()->selector;
3484 Drag::start_grab (event, cursor);
3486 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3490 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3492 framepos_t start = 0;
3494 ArdourCanvas::SimpleRect *crect;
3496 switch (_operation) {
3497 case CreateRangeMarker:
3498 crect = _editor->range_bar_drag_rect;
3500 case CreateTransportMarker:
3501 crect = _editor->transport_bar_drag_rect;
3503 case CreateCDMarker:
3504 crect = _editor->cd_marker_bar_drag_rect;
3507 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3512 framepos_t const pf = adjusted_current_frame (event);
3514 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3515 framepos_t grab = grab_frame ();
3516 _editor->snap_to (grab);
3518 if (pf < grab_frame()) {
3526 /* first drag: Either add to the selection
3527 or create a new selection.
3532 _editor->temp_location->set (start, end);
3536 update_item (_editor->temp_location);
3538 //_drag_rect->raise_to_top();
3543 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3544 _editor->start_canvas_autoscroll (1, 0);
3548 _editor->temp_location->set (start, end);
3550 double x1 = _editor->frame_to_pixel (start);
3551 double x2 = _editor->frame_to_pixel (end);
3552 crect->property_x1() = x1;
3553 crect->property_x2() = x2;
3555 update_item (_editor->temp_location);
3558 _editor->show_verbose_time_cursor (pf, 10);
3563 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3565 Location * newloc = 0;
3569 if (movement_occurred) {
3570 motion (event, false);
3573 switch (_operation) {
3574 case CreateRangeMarker:
3575 case CreateCDMarker:
3577 _editor->begin_reversible_command (_("new range marker"));
3578 XMLNode &before = _editor->session()->locations()->get_state();
3579 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3580 if (_operation == CreateCDMarker) {
3581 flags = Location::IsRangeMarker | Location::IsCDMarker;
3582 _editor->cd_marker_bar_drag_rect->hide();
3585 flags = Location::IsRangeMarker;
3586 _editor->range_bar_drag_rect->hide();
3588 newloc = new Location (
3589 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3592 _editor->session()->locations()->add (newloc, true);
3593 XMLNode &after = _editor->session()->locations()->get_state();
3594 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3595 _editor->commit_reversible_command ();
3599 case CreateTransportMarker:
3600 // popup menu to pick loop or punch
3601 _editor->new_transport_marker_context_menu (&event->button, _item);
3605 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3607 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3612 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3614 if (end == max_framepos) {
3615 end = _editor->session()->current_end_frame ();
3618 if (start == max_framepos) {
3619 start = _editor->session()->current_start_frame ();
3622 switch (_editor->mouse_mode) {
3624 /* find the two markers on either side and then make the selection from it */
3625 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3629 /* find the two markers on either side of the click and make the range out of it */
3630 _editor->selection->set (start, end);
3639 _editor->stop_canvas_autoscroll ();
3643 RangeMarkerBarDrag::aborted (bool)
3649 RangeMarkerBarDrag::update_item (Location* location)
3651 double const x1 = _editor->frame_to_pixel (location->start());
3652 double const x2 = _editor->frame_to_pixel (location->end());
3654 _drag_rect->property_x1() = x1;
3655 _drag_rect->property_x2() = x2;
3658 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3662 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3666 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3668 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3669 Drag::start_grab (event, _editor->cursors()->zoom_out);
3672 Drag::start_grab (event, _editor->cursors()->zoom_in);
3676 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3680 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3685 framepos_t const pf = adjusted_current_frame (event);
3687 framepos_t grab = grab_frame ();
3688 _editor->snap_to_with_modifier (grab, event);
3690 /* base start and end on initial click position */
3702 _editor->zoom_rect->show();
3703 _editor->zoom_rect->raise_to_top();
3706 _editor->reposition_zoom_rect(start, end);
3708 _editor->show_verbose_time_cursor (pf, 10);
3713 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3715 if (movement_occurred) {
3716 motion (event, false);
3718 if (grab_frame() < last_pointer_frame()) {
3719 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3721 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3724 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3727 _editor->zoom_rect->hide();
3731 MouseZoomDrag::aborted (bool)
3733 _editor->zoom_rect->hide ();
3736 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3738 , _cumulative_dx (0)
3739 , _cumulative_dy (0)
3741 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3743 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3744 _region = &_primary->region_view ();
3745 _note_height = _region->midi_stream_view()->note_height ();
3749 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3751 Drag::start_grab (event);
3753 if (!(_was_selected = _primary->selected())) {
3755 /* tertiary-click means extend selection - we'll do that on button release,
3756 so don't add it here, because otherwise we make it hard to figure
3757 out the "extend-to" range.
3760 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3763 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3766 _region->note_selected (_primary, true);
3768 _region->unique_select (_primary);
3774 /** @return Current total drag x change in frames */
3776 NoteDrag::total_dx () const
3779 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3781 /* primary note time */
3782 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3784 /* new time of the primary note relative to the region position */
3785 frameoffset_t const st = n + dx;
3787 /* snap and return corresponding delta */
3788 return _region->snap_frame_to_frame (st) - n;
3791 /** @return Current total drag y change in notes */
3793 NoteDrag::total_dy () const
3795 /* this is `backwards' to make increasing note number go in the right direction */
3796 double const dy = _drags->current_pointer_y() - grab_y();
3801 if (abs (dy) >= _note_height) {
3803 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3805 ndy = (int8_t) floor (dy / _note_height / 2.0);
3809 /* more positive value = higher pitch and higher y-axis position on track,
3810 which is the inverse of the X-centric geometric universe
3817 NoteDrag::motion (GdkEvent *, bool)
3819 /* Total change in x and y since the start of the drag */
3820 frameoffset_t const dx = total_dx ();
3821 int8_t const dy = -total_dy ();
3823 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3824 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3825 double const tdy = dy * _note_height - _cumulative_dy;
3828 _cumulative_dx += tdx;
3829 _cumulative_dy += tdy;
3831 int8_t note_delta = total_dy();
3833 _region->move_selection (tdx, tdy, note_delta);
3836 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3837 (int) floor (_primary->note()->note() + note_delta));
3839 _editor->show_verbose_canvas_cursor_with (buf);
3844 NoteDrag::finished (GdkEvent* ev, bool moved)
3847 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3849 if (_was_selected) {
3850 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3852 _region->note_deselected (_primary);
3855 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3856 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3858 if (!extend && !add && _region->selection_size() > 1) {
3859 _region->unique_select (_primary);
3860 } else if (extend) {
3861 _region->note_selected (_primary, true, true);
3863 /* it was added during button press */
3868 _region->note_dropped (_primary, total_dx(), total_dy());
3873 NoteDrag::aborted (bool)
3878 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3879 : Drag (editor, item)
3881 , _nothing_to_drag (false)
3883 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3885 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3888 /* get all lines in the automation view */
3889 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3891 /* find those that overlap the ranges being dragged */
3892 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3893 while (i != lines.end ()) {
3894 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3897 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3899 /* check this range against all the AudioRanges that we are using */
3900 list<AudioRange>::const_iterator k = _ranges.begin ();
3901 while (k != _ranges.end()) {
3902 if (k->coverage (r.first, r.second) != OverlapNone) {
3908 /* add it to our list if it overlaps at all */
3909 if (k != _ranges.end()) {
3914 _lines.push_back (n);
3920 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3924 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3926 Drag::start_grab (event, cursor);
3928 /* Get line states before we start changing things */
3929 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3930 i->state = &i->line->get_state ();
3933 if (_ranges.empty()) {
3935 /* No selected time ranges: drag all points */
3936 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3937 uint32_t const N = i->line->npoints ();
3938 for (uint32_t j = 0; j < N; ++j) {
3939 i->points.push_back (i->line->nth (j));
3945 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3947 framecnt_t const half = (i->start + i->end) / 2;
3949 /* find the line that this audio range starts in */
3950 list<Line>::iterator j = _lines.begin();
3951 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3955 if (j != _lines.end()) {
3956 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3958 /* j is the line that this audio range starts in; fade into it;
3959 64 samples length plucked out of thin air.
3962 framepos_t a = i->start + 64;
3967 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3968 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3970 the_list->add (p, the_list->eval (p));
3971 j->line->add_always_in_view (p);
3972 the_list->add (q, the_list->eval (q));
3973 j->line->add_always_in_view (q);
3976 /* same thing for the end */
3979 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3983 if (j != _lines.end()) {
3984 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3986 /* j is the line that this audio range starts in; fade out of it;
3987 64 samples length plucked out of thin air.
3990 framepos_t b = i->end - 64;
3995 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3996 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3998 the_list->add (p, the_list->eval (p));
3999 j->line->add_always_in_view (p);
4000 the_list->add (q, the_list->eval (q));
4001 j->line->add_always_in_view (q);
4005 _nothing_to_drag = true;
4007 /* Find all the points that should be dragged and put them in the relevant
4008 points lists in the Line structs.
4011 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4013 uint32_t const N = i->line->npoints ();
4014 for (uint32_t j = 0; j < N; ++j) {
4016 /* here's a control point on this line */
4017 ControlPoint* p = i->line->nth (j);
4018 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4020 /* see if it's inside a range */
4021 list<AudioRange>::const_iterator k = _ranges.begin ();
4022 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4026 if (k != _ranges.end()) {
4027 /* dragging this point */
4028 _nothing_to_drag = false;
4029 i->points.push_back (p);
4035 if (_nothing_to_drag) {
4039 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4040 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4045 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4047 if (_nothing_to_drag) {
4051 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4052 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4054 /* we are ignoring x position for this drag, so we can just pass in anything */
4055 i->line->drag_motion (0, f, true, false);
4060 AutomationRangeDrag::finished (GdkEvent* event, bool)
4062 if (_nothing_to_drag) {
4066 motion (event, false);
4067 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4068 i->line->end_drag ();
4069 i->line->clear_always_in_view ();
4072 _editor->session()->commit_reversible_command ();
4076 AutomationRangeDrag::aborted (bool)
4078 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4079 i->line->clear_always_in_view ();
4084 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4087 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4088 layer = v->region()->layer ();
4089 initial_y = v->get_canvas_group()->property_y ();
4090 initial_playlist = v->region()->playlist ();
4091 initial_position = v->region()->position ();
4092 initial_end = v->region()->position () + v->region()->length ();
4095 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4099 , _cumulative_dx (0)
4101 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4105 PatchChangeDrag::motion (GdkEvent* ev, bool)
4107 framepos_t f = adjusted_current_frame (ev);
4108 boost::shared_ptr<Region> r = _region_view->region ();
4109 f = max (f, r->position ());
4110 f = min (f, r->last_frame ());
4112 framecnt_t const dxf = f - grab_frame();
4113 double const dxu = _editor->frame_to_unit (dxf);
4114 _patch_change->move (dxu - _cumulative_dx, 0);
4115 _cumulative_dx = dxu;
4119 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4121 if (!movement_occurred) {
4125 boost::shared_ptr<Region> r (_region_view->region ());
4127 framepos_t f = adjusted_current_frame (ev);
4128 f = max (f, r->position ());
4129 f = min (f, r->last_frame ());
4131 _region_view->move_patch_change (
4133 _region_view->frames_to_beats (f - r->position() - r->start())
4138 PatchChangeDrag::aborted (bool)
4140 _patch_change->move (-_cumulative_dx, 0);
4144 PatchChangeDrag::setup_pointer_frame_offset ()
4146 boost::shared_ptr<Region> region = _region_view->region ();
4147 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();