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);
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);
3044 _editor->begin_reversible_command (_("rubberband selection"));
3046 if (grab_frame() < last_pointer_frame()) {
3047 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3049 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3053 _editor->commit_reversible_command ();
3057 if (!getenv("ARDOUR_SAE")) {
3058 _editor->selection->clear_tracks();
3060 _editor->selection->clear_regions();
3061 _editor->selection->clear_points ();
3062 _editor->selection->clear_lines ();
3065 _editor->rubberband_rect->hide();
3069 RubberbandSelectDrag::aborted (bool)
3071 _editor->rubberband_rect->hide ();
3074 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3075 : RegionDrag (e, i, p, v)
3077 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3081 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3083 Drag::start_grab (event, cursor);
3085 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3089 TimeFXDrag::motion (GdkEvent* event, bool)
3091 RegionView* rv = _primary;
3093 framepos_t const pf = adjusted_current_frame (event);
3095 if (pf > rv->region()->position()) {
3096 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3099 _editor->show_verbose_time_cursor (pf, 10);
3103 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3105 _primary->get_time_axis_view().hide_timestretch ();
3107 if (!movement_occurred) {
3111 if (last_pointer_frame() < _primary->region()->position()) {
3112 /* backwards drag of the left edge - not usable */
3116 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3118 float percentage = (double) newlen / (double) _primary->region()->length();
3120 #ifndef USE_RUBBERBAND
3121 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3122 if (_primary->region()->data_type() == DataType::AUDIO) {
3123 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3127 _editor->begin_reversible_command (_("timestretch"));
3129 // XXX how do timeFX on multiple regions ?
3134 if (_editor->time_stretch (rs, percentage) == -1) {
3135 error << _("An error occurred while executing time stretch operation") << endmsg;
3140 TimeFXDrag::aborted (bool)
3142 _primary->get_time_axis_view().hide_timestretch ();
3145 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3148 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3152 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3154 Drag::start_grab (event);
3158 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3160 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3164 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3166 if (movement_occurred && _editor->session()) {
3167 /* make sure we stop */
3168 _editor->session()->request_transport_speed (0.0);
3173 ScrubDrag::aborted (bool)
3178 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3182 , _original_pointer_time_axis (-1)
3183 , _last_pointer_time_axis (-1)
3185 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3189 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3191 if (_editor->session() == 0) {
3195 Gdk::Cursor* cursor = 0;
3197 switch (_operation) {
3198 case CreateSelection:
3199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3204 cursor = _editor->cursors()->selector;
3205 Drag::start_grab (event, cursor);
3208 case SelectionStartTrim:
3209 if (_editor->clicked_axisview) {
3210 _editor->clicked_axisview->order_selection_trims (_item, true);
3212 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3215 case SelectionEndTrim:
3216 if (_editor->clicked_axisview) {
3217 _editor->clicked_axisview->order_selection_trims (_item, false);
3219 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3223 Drag::start_grab (event, cursor);
3227 if (_operation == SelectionMove) {
3228 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3230 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3233 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3237 SelectionDrag::setup_pointer_frame_offset ()
3239 switch (_operation) {
3240 case CreateSelection:
3241 _pointer_frame_offset = 0;
3244 case SelectionStartTrim:
3246 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3249 case SelectionEndTrim:
3250 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3256 SelectionDrag::motion (GdkEvent* event, bool first_move)
3258 framepos_t start = 0;
3262 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3263 if (pending_time_axis.first == 0) {
3267 framepos_t const pending_position = adjusted_current_frame (event);
3269 /* only alter selection if things have changed */
3271 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3275 switch (_operation) {
3276 case CreateSelection:
3278 framepos_t grab = grab_frame ();
3281 _editor->snap_to (grab);
3284 if (pending_position < grab_frame()) {
3285 start = pending_position;
3288 end = pending_position;
3292 /* first drag: Either add to the selection
3293 or create a new selection
3299 /* adding to the selection */
3300 _editor->set_selected_track_as_side_effect (Selection::Add);
3301 //_editor->selection->add (_editor->clicked_axisview);
3302 _editor->clicked_selection = _editor->selection->add (start, end);
3307 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3308 //_editor->selection->set (_editor->clicked_axisview);
3309 _editor->set_selected_track_as_side_effect (Selection::Set);
3312 _editor->clicked_selection = _editor->selection->set (start, end);
3316 /* select the track that we're in */
3317 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3318 // _editor->set_selected_track_as_side_effect (Selection::Add);
3319 _editor->selection->add (pending_time_axis.first);
3320 _added_time_axes.push_back (pending_time_axis.first);
3323 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3324 tracks that we selected in the first place.
3327 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3328 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3330 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3331 while (i != _added_time_axes.end()) {
3333 list<TimeAxisView*>::iterator tmp = i;
3336 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3337 _editor->selection->remove (*i);
3338 _added_time_axes.remove (*i);
3347 case SelectionStartTrim:
3349 start = _editor->selection->time[_editor->clicked_selection].start;
3350 end = _editor->selection->time[_editor->clicked_selection].end;
3352 if (pending_position > end) {
3355 start = pending_position;
3359 case SelectionEndTrim:
3361 start = _editor->selection->time[_editor->clicked_selection].start;
3362 end = _editor->selection->time[_editor->clicked_selection].end;
3364 if (pending_position < start) {
3367 end = pending_position;
3374 start = _editor->selection->time[_editor->clicked_selection].start;
3375 end = _editor->selection->time[_editor->clicked_selection].end;
3377 length = end - start;
3379 start = pending_position;
3380 _editor->snap_to (start);
3382 end = start + length;
3387 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3388 _editor->start_canvas_autoscroll (1, 0);
3392 _editor->selection->replace (_editor->clicked_selection, start, end);
3395 if (_operation == SelectionMove) {
3396 _editor->show_verbose_time_cursor(start, 10);
3398 _editor->show_verbose_time_cursor(pending_position, 10);
3403 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3405 Session* s = _editor->session();
3407 if (movement_occurred) {
3408 motion (event, false);
3409 /* XXX this is not object-oriented programming at all. ick */
3410 if (_editor->selection->time.consolidate()) {
3411 _editor->selection->TimeChanged ();
3414 /* XXX what if its a music time selection? */
3415 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3416 s->request_play_range (&_editor->selection->time, true);
3421 /* just a click, no pointer movement.*/
3423 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3424 _editor->selection->clear_time();
3427 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3428 _editor->selection->set (_editor->clicked_axisview);
3431 if (s && s->get_play_range () && s->transport_rolling()) {
3432 s->request_stop (false, false);
3437 _editor->stop_canvas_autoscroll ();
3441 SelectionDrag::aborted (bool)
3446 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3451 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3453 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3454 physical_screen_height (_editor->get_window()));
3455 _drag_rect->hide ();
3457 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3458 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3462 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3464 if (_editor->session() == 0) {
3468 Gdk::Cursor* cursor = 0;
3470 if (!_editor->temp_location) {
3471 _editor->temp_location = new Location (*_editor->session());
3474 switch (_operation) {
3475 case CreateRangeMarker:
3476 case CreateTransportMarker:
3477 case CreateCDMarker:
3479 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3484 cursor = _editor->cursors()->selector;
3488 Drag::start_grab (event, cursor);
3490 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3494 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3496 framepos_t start = 0;
3498 ArdourCanvas::SimpleRect *crect;
3500 switch (_operation) {
3501 case CreateRangeMarker:
3502 crect = _editor->range_bar_drag_rect;
3504 case CreateTransportMarker:
3505 crect = _editor->transport_bar_drag_rect;
3507 case CreateCDMarker:
3508 crect = _editor->cd_marker_bar_drag_rect;
3511 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3516 framepos_t const pf = adjusted_current_frame (event);
3518 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3519 framepos_t grab = grab_frame ();
3520 _editor->snap_to (grab);
3522 if (pf < grab_frame()) {
3530 /* first drag: Either add to the selection
3531 or create a new selection.
3536 _editor->temp_location->set (start, end);
3540 update_item (_editor->temp_location);
3542 //_drag_rect->raise_to_top();
3547 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3548 _editor->start_canvas_autoscroll (1, 0);
3552 _editor->temp_location->set (start, end);
3554 double x1 = _editor->frame_to_pixel (start);
3555 double x2 = _editor->frame_to_pixel (end);
3556 crect->property_x1() = x1;
3557 crect->property_x2() = x2;
3559 update_item (_editor->temp_location);
3562 _editor->show_verbose_time_cursor (pf, 10);
3567 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3569 Location * newloc = 0;
3573 if (movement_occurred) {
3574 motion (event, false);
3577 switch (_operation) {
3578 case CreateRangeMarker:
3579 case CreateCDMarker:
3581 _editor->begin_reversible_command (_("new range marker"));
3582 XMLNode &before = _editor->session()->locations()->get_state();
3583 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3584 if (_operation == CreateCDMarker) {
3585 flags = Location::IsRangeMarker | Location::IsCDMarker;
3586 _editor->cd_marker_bar_drag_rect->hide();
3589 flags = Location::IsRangeMarker;
3590 _editor->range_bar_drag_rect->hide();
3592 newloc = new Location (
3593 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3596 _editor->session()->locations()->add (newloc, true);
3597 XMLNode &after = _editor->session()->locations()->get_state();
3598 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3599 _editor->commit_reversible_command ();
3603 case CreateTransportMarker:
3604 // popup menu to pick loop or punch
3605 _editor->new_transport_marker_context_menu (&event->button, _item);
3609 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3611 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3616 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3618 if (end == max_framepos) {
3619 end = _editor->session()->current_end_frame ();
3622 if (start == max_framepos) {
3623 start = _editor->session()->current_start_frame ();
3626 switch (_editor->mouse_mode) {
3628 /* find the two markers on either side and then make the selection from it */
3629 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3633 /* find the two markers on either side of the click and make the range out of it */
3634 _editor->selection->set (start, end);
3643 _editor->stop_canvas_autoscroll ();
3647 RangeMarkerBarDrag::aborted (bool)
3653 RangeMarkerBarDrag::update_item (Location* location)
3655 double const x1 = _editor->frame_to_pixel (location->start());
3656 double const x2 = _editor->frame_to_pixel (location->end());
3658 _drag_rect->property_x1() = x1;
3659 _drag_rect->property_x2() = x2;
3662 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3666 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3670 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3672 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3673 Drag::start_grab (event, _editor->cursors()->zoom_out);
3676 Drag::start_grab (event, _editor->cursors()->zoom_in);
3680 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3684 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3689 framepos_t const pf = adjusted_current_frame (event);
3691 framepos_t grab = grab_frame ();
3692 _editor->snap_to_with_modifier (grab, event);
3694 /* base start and end on initial click position */
3706 _editor->zoom_rect->show();
3707 _editor->zoom_rect->raise_to_top();
3710 _editor->reposition_zoom_rect(start, end);
3712 _editor->show_verbose_time_cursor (pf, 10);
3717 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3719 if (movement_occurred) {
3720 motion (event, false);
3722 if (grab_frame() < last_pointer_frame()) {
3723 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3725 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3728 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3731 _editor->zoom_rect->hide();
3735 MouseZoomDrag::aborted (bool)
3737 _editor->zoom_rect->hide ();
3740 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3742 , _cumulative_dx (0)
3743 , _cumulative_dy (0)
3745 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3747 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3748 _region = &_primary->region_view ();
3749 _note_height = _region->midi_stream_view()->note_height ();
3753 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3755 Drag::start_grab (event);
3757 if (!(_was_selected = _primary->selected())) {
3759 /* tertiary-click means extend selection - we'll do that on button release,
3760 so don't add it here, because otherwise we make it hard to figure
3761 out the "extend-to" range.
3764 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3767 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3770 _region->note_selected (_primary, true);
3772 _region->unique_select (_primary);
3778 /** @return Current total drag x change in frames */
3780 NoteDrag::total_dx () const
3783 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3785 /* primary note time */
3786 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3788 /* new time of the primary note relative to the region position */
3789 frameoffset_t const st = n + dx;
3791 /* snap and return corresponding delta */
3792 return _region->snap_frame_to_frame (st) - n;
3795 /** @return Current total drag y change in notes */
3797 NoteDrag::total_dy () const
3799 /* this is `backwards' to make increasing note number go in the right direction */
3800 double const dy = _drags->current_pointer_y() - grab_y();
3805 if (abs (dy) >= _note_height) {
3807 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3809 ndy = (int8_t) floor (dy / _note_height / 2.0);
3813 /* more positive value = higher pitch and higher y-axis position on track,
3814 which is the inverse of the X-centric geometric universe
3821 NoteDrag::motion (GdkEvent *, bool)
3823 /* Total change in x and y since the start of the drag */
3824 frameoffset_t const dx = total_dx ();
3825 int8_t const dy = -total_dy ();
3827 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3828 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3829 double const tdy = dy * _note_height - _cumulative_dy;
3832 _cumulative_dx += tdx;
3833 _cumulative_dy += tdy;
3835 int8_t note_delta = total_dy();
3837 _region->move_selection (tdx, tdy, note_delta);
3840 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3841 (int) floor (_primary->note()->note() + note_delta));
3843 _editor->show_verbose_canvas_cursor_with (buf);
3848 NoteDrag::finished (GdkEvent* ev, bool moved)
3851 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3853 if (_was_selected) {
3854 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3856 _region->note_deselected (_primary);
3859 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3860 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3862 if (!extend && !add && _region->selection_size() > 1) {
3863 _region->unique_select (_primary);
3864 } else if (extend) {
3865 _region->note_selected (_primary, true, true);
3867 /* it was added during button press */
3872 _region->note_dropped (_primary, total_dx(), total_dy());
3877 NoteDrag::aborted (bool)
3882 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3883 : Drag (editor, item)
3885 , _nothing_to_drag (false)
3887 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3889 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3892 /* get all lines in the automation view */
3893 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3895 /* find those that overlap the ranges being dragged */
3896 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3897 while (i != lines.end ()) {
3898 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3901 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3903 /* check this range against all the AudioRanges that we are using */
3904 list<AudioRange>::const_iterator k = _ranges.begin ();
3905 while (k != _ranges.end()) {
3906 if (k->coverage (r.first, r.second) != OverlapNone) {
3912 /* add it to our list if it overlaps at all */
3913 if (k != _ranges.end()) {
3918 _lines.push_back (n);
3924 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3928 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3930 Drag::start_grab (event, cursor);
3932 /* Get line states before we start changing things */
3933 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3934 i->state = &i->line->get_state ();
3937 if (_ranges.empty()) {
3939 /* No selected time ranges: drag all points */
3940 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3941 uint32_t const N = i->line->npoints ();
3942 for (uint32_t j = 0; j < N; ++j) {
3943 i->points.push_back (i->line->nth (j));
3949 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3951 framecnt_t const half = (i->start + i->end) / 2;
3953 /* find the line that this audio range starts in */
3954 list<Line>::iterator j = _lines.begin();
3955 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3959 if (j != _lines.end()) {
3960 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3962 /* j is the line that this audio range starts in; fade into it;
3963 64 samples length plucked out of thin air.
3966 framepos_t a = i->start + 64;
3971 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3972 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3974 the_list->add (p, the_list->eval (p));
3975 j->line->add_always_in_view (p);
3976 the_list->add (q, the_list->eval (q));
3977 j->line->add_always_in_view (q);
3980 /* same thing for the end */
3983 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3987 if (j != _lines.end()) {
3988 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3990 /* j is the line that this audio range starts in; fade out of it;
3991 64 samples length plucked out of thin air.
3994 framepos_t b = i->end - 64;
3999 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4000 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4002 the_list->add (p, the_list->eval (p));
4003 j->line->add_always_in_view (p);
4004 the_list->add (q, the_list->eval (q));
4005 j->line->add_always_in_view (q);
4009 _nothing_to_drag = true;
4011 /* Find all the points that should be dragged and put them in the relevant
4012 points lists in the Line structs.
4015 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4017 uint32_t const N = i->line->npoints ();
4018 for (uint32_t j = 0; j < N; ++j) {
4020 /* here's a control point on this line */
4021 ControlPoint* p = i->line->nth (j);
4022 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4024 /* see if it's inside a range */
4025 list<AudioRange>::const_iterator k = _ranges.begin ();
4026 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4030 if (k != _ranges.end()) {
4031 /* dragging this point */
4032 _nothing_to_drag = false;
4033 i->points.push_back (p);
4039 if (_nothing_to_drag) {
4043 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4044 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4049 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4051 if (_nothing_to_drag) {
4055 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4056 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4058 /* we are ignoring x position for this drag, so we can just pass in anything */
4059 i->line->drag_motion (0, f, true, false);
4064 AutomationRangeDrag::finished (GdkEvent* event, bool)
4066 if (_nothing_to_drag) {
4070 motion (event, false);
4071 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4072 i->line->end_drag ();
4073 i->line->clear_always_in_view ();
4076 _editor->session()->commit_reversible_command ();
4080 AutomationRangeDrag::aborted (bool)
4082 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4083 i->line->clear_always_in_view ();
4088 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4091 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4092 layer = v->region()->layer ();
4093 initial_y = v->get_canvas_group()->property_y ();
4094 initial_playlist = v->region()->playlist ();
4095 initial_position = v->region()->position ();
4096 initial_end = v->region()->position () + v->region()->length ();
4099 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4103 , _cumulative_dx (0)
4105 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4109 PatchChangeDrag::motion (GdkEvent* ev, bool)
4111 framepos_t f = adjusted_current_frame (ev);
4112 boost::shared_ptr<Region> r = _region_view->region ();
4113 f = max (f, r->position ());
4114 f = min (f, r->last_frame ());
4116 framecnt_t const dxf = f - grab_frame();
4117 double const dxu = _editor->frame_to_unit (dxf);
4118 _patch_change->move (dxu - _cumulative_dx, 0);
4119 _cumulative_dx = dxu;
4123 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4125 if (!movement_occurred) {
4129 boost::shared_ptr<Region> r (_region_view->region ());
4131 framepos_t f = adjusted_current_frame (ev);
4132 f = max (f, r->position ());
4133 f = min (f, r->last_frame ());
4135 _region_view->move_patch_change (
4137 _region_view->frames_to_beats (f - r->position() - r->start())
4142 PatchChangeDrag::aborted (bool)
4144 _patch_change->move (-_cumulative_dx, 0);
4148 PatchChangeDrag::setup_pointer_frame_offset ()
4150 boost::shared_ptr<Region> region = _region_view->region ();
4151 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();