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"
57 #include "verbose_cursor.h"
60 using namespace ARDOUR;
63 using namespace Gtkmm2ext;
64 using namespace Editing;
65 using namespace ArdourCanvas;
67 using Gtkmm2ext::Keyboard;
69 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
71 DragManager::DragManager (Editor* e)
74 , _current_pointer_frame (0)
79 DragManager::~DragManager ()
84 /** Call abort for each active drag */
90 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
95 if (!_drags.empty ()) {
96 _editor->set_follow_playhead (_old_follow_playhead, false);
105 DragManager::add (Drag* d)
107 d->set_manager (this);
108 _drags.push_back (d);
112 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
114 d->set_manager (this);
115 _drags.push_back (d);
120 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
122 /* Prevent follow playhead during the drag to be nice to the user */
123 _old_follow_playhead = _editor->follow_playhead ();
124 _editor->set_follow_playhead (false);
126 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
128 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
129 (*i)->start_grab (e, c);
133 /** Call end_grab for each active drag.
134 * @return true if any drag reported movement having occurred.
137 DragManager::end_grab (GdkEvent* e)
142 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
143 bool const t = (*i)->end_grab (e);
154 _editor->set_follow_playhead (_old_follow_playhead, false);
160 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
164 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
166 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
167 bool const t = (*i)->motion_handler (e, from_autoscroll);
178 DragManager::have_item (ArdourCanvas::Item* i) const
180 list<Drag*>::const_iterator j = _drags.begin ();
181 while (j != _drags.end() && (*j)->item () != i) {
185 return j != _drags.end ();
188 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
191 , _pointer_frame_offset (0)
192 , _move_threshold_passed (false)
193 , _raw_grab_frame (0)
195 , _last_pointer_frame (0)
201 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
207 cursor = _editor->which_grabber_cursor ();
210 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
214 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
217 cursor = _editor->which_grabber_cursor ();
220 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
222 if (Keyboard::is_button2_event (&event->button)) {
223 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
224 _y_constrained = true;
225 _x_constrained = false;
227 _y_constrained = false;
228 _x_constrained = true;
231 _x_constrained = false;
232 _y_constrained = false;
235 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
236 setup_pointer_frame_offset ();
237 _grab_frame = adjusted_frame (_raw_grab_frame, event);
238 _last_pointer_frame = _grab_frame;
239 _last_pointer_x = _grab_x;
240 _last_pointer_y = _grab_y;
242 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
246 if (_editor->session() && _editor->session()->transport_rolling()) {
249 _was_rolling = false;
252 switch (_editor->snap_type()) {
253 case SnapToRegionStart:
254 case SnapToRegionEnd:
255 case SnapToRegionSync:
256 case SnapToRegionBoundary:
257 _editor->build_region_boundary_cache ();
264 /** Call to end a drag `successfully'. Ungrabs item and calls
265 * subclass' finished() method.
267 * @param event GDK event, or 0.
268 * @return true if some movement occurred, otherwise false.
271 Drag::end_grab (GdkEvent* event)
273 _editor->stop_canvas_autoscroll ();
275 _item->ungrab (event ? event->button.time : 0);
277 finished (event, _move_threshold_passed);
279 _editor->verbose_cursor()->hide ();
281 return _move_threshold_passed;
285 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
289 if (f > _pointer_frame_offset) {
290 pos = f - _pointer_frame_offset;
294 _editor->snap_to_with_modifier (pos, event);
301 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
303 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
307 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
309 /* check to see if we have moved in any way that matters since the last motion event */
310 if (_move_threshold_passed &&
311 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
312 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
316 pair<framecnt_t, int> const threshold = move_threshold ();
318 bool const old_move_threshold_passed = _move_threshold_passed;
320 if (!from_autoscroll && !_move_threshold_passed) {
322 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
323 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
325 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
328 if (active (_editor->mouse_mode) && _move_threshold_passed) {
330 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
331 if (!from_autoscroll) {
332 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
335 motion (event, _move_threshold_passed != old_move_threshold_passed);
337 _last_pointer_x = _drags->current_pointer_x ();
338 _last_pointer_y = _drags->current_pointer_y ();
339 _last_pointer_frame = adjusted_current_frame (event);
347 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
355 aborted (_move_threshold_passed);
357 _editor->stop_canvas_autoscroll ();
358 _editor->verbose_cursor()->hide ();
362 Drag::show_verbose_cursor_time (framepos_t frame)
364 _editor->verbose_cursor()->set_time (
366 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
367 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
370 _editor->verbose_cursor()->show ();
374 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
376 _editor->verbose_cursor()->show (xoffset);
378 _editor->verbose_cursor()->set_duration (
380 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
381 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
386 Drag::show_verbose_cursor_text (string const & text)
388 _editor->verbose_cursor()->show ();
390 _editor->verbose_cursor()->set (
392 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
393 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
398 struct EditorOrderTimeAxisViewSorter {
399 bool operator() (TimeAxisView* a, TimeAxisView* b) {
400 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
401 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
403 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
407 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
411 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
413 /* Make a list of non-hidden tracks to refer to during the drag */
415 TrackViewList track_views = _editor->track_views;
416 track_views.sort (EditorOrderTimeAxisViewSorter ());
418 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
419 if (!(*i)->hidden()) {
421 _time_axis_views.push_back (*i);
423 TimeAxisView::Children children_list = (*i)->get_child_list ();
424 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
425 _time_axis_views.push_back (j->get());
430 /* the list of views can be empty at this point if this is a region list-insert drag
433 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
434 _views.push_back (DraggingView (*i, this));
437 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
441 RegionDrag::region_going_away (RegionView* v)
443 list<DraggingView>::iterator i = _views.begin ();
444 while (i != _views.end() && i->view != v) {
448 if (i != _views.end()) {
453 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
455 RegionDrag::find_time_axis_view (TimeAxisView* t) const
458 int const N = _time_axis_views.size ();
459 while (i < N && _time_axis_views[i] != t) {
470 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
471 : RegionDrag (e, i, p, v),
480 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
482 Drag::start_grab (event, cursor);
484 show_verbose_cursor_time (_last_frame_position);
486 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
487 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
488 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
492 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
494 /* compute the amount of pointer motion in frames, and where
495 the region would be if we moved it by that much.
497 *pending_region_position = adjusted_current_frame (event);
499 framepos_t sync_frame;
500 framecnt_t sync_offset;
503 sync_offset = _primary->region()->sync_offset (sync_dir);
505 /* we don't handle a sync point that lies before zero.
507 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
509 sync_frame = *pending_region_position + (sync_dir*sync_offset);
511 _editor->snap_to_with_modifier (sync_frame, event);
513 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
516 *pending_region_position = _last_frame_position;
519 if (*pending_region_position > max_framepos - _primary->region()->length()) {
520 *pending_region_position = _last_frame_position;
525 /* in locked edit mode, reverse the usual meaning of _x_constrained */
526 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
528 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
530 /* x movement since last time (in pixels) */
531 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
533 /* total x movement */
534 framecnt_t total_dx = *pending_region_position;
535 if (regions_came_from_canvas()) {
536 total_dx = total_dx - grab_frame ();
539 /* check that no regions have gone off the start of the session */
540 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
541 if ((i->view->region()->position() + total_dx) < 0) {
543 *pending_region_position = _last_frame_position;
548 _last_frame_position = *pending_region_position;
555 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
557 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
558 int const n = i->time_axis_view + delta_track;
559 if (n < 0 || n >= int (_time_axis_views.size())) {
560 /* off the top or bottom track */
564 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
565 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
566 /* not a track, or the wrong type */
570 int const l = i->layer + delta_layer;
571 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
572 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
573 If it has, the layers will be munged later anyway, so it's ok.
579 /* all regions being dragged are ok with this change */
584 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
586 assert (!_views.empty ());
588 /* Find the TimeAxisView that the pointer is now over */
589 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
591 /* Bail early if we're not over a track */
592 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
593 if (!rtv || !rtv->is_track()) {
594 _editor->verbose_cursor()->hide ();
598 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
600 /* Here's the current pointer position in terms of time axis view and layer */
601 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
602 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
604 /* Work out the change in x */
605 framepos_t pending_region_position;
606 double const x_delta = compute_x_delta (event, &pending_region_position);
608 /* Work out the change in y */
609 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
610 int delta_layer = current_pointer_layer - _last_pointer_layer;
612 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
613 /* this y movement is not allowed, so do no y movement this time */
614 delta_time_axis_view = 0;
618 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
619 /* haven't reached next snap point, and we're not switching
620 trackviews nor layers. nothing to do.
625 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
627 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
629 RegionView* rv = i->view;
631 if (rv->region()->locked()) {
637 /* here we are calculating the y distance from the
638 top of the first track view to the top of the region
639 area of the track view that we're working on */
641 /* this x value is just a dummy value so that we have something
646 /* distance from the top of this track view to the region area
647 of our track view is always 1 */
651 /* convert to world coordinates, ie distance from the top of
654 rv->get_canvas_frame()->i2w (ix1, iy1);
656 /* compensate for the ruler section and the vertical scrollbar position */
657 iy1 += _editor->get_trackview_group_vertical_offset ();
659 // hide any dependent views
661 rv->get_time_axis_view().hide_dependent_views (*rv);
664 reparent to a non scrolling group so that we can keep the
665 region selection above all time axis views.
666 reparenting means we have to move the rv as the two
667 parent groups have different coordinates.
670 rv->get_canvas_group()->property_y() = iy1 - 1;
671 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
673 rv->fake_set_opaque (true);
676 /* Work out the change in y position of this region view */
680 /* If we have moved tracks, we'll fudge the layer delta so that the
681 region gets moved back onto layer 0 on its new track; this avoids
682 confusion when dragging regions from non-zero layers onto different
685 int this_delta_layer = delta_layer;
686 if (delta_time_axis_view != 0) {
687 this_delta_layer = - i->layer;
690 /* Move this region to layer 0 on its old track */
691 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
692 if (lv->layer_display() == Stacked) {
693 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
696 /* Now move it to its right layer on the current track */
697 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
698 if (cv->layer_display() == Stacked) {
699 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
703 if (delta_time_axis_view > 0) {
704 for (int j = 0; j < delta_time_axis_view; ++j) {
705 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
708 /* start by subtracting the height of the track above where we are now */
709 for (int j = 1; j <= -delta_time_axis_view; ++j) {
710 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
715 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
717 /* Update the DraggingView */
718 i->time_axis_view += delta_time_axis_view;
719 i->layer += this_delta_layer;
722 _editor->mouse_brush_insert_region (rv, pending_region_position);
724 rv->move (x_delta, y_delta);
727 } /* foreach region */
729 _total_x_delta += x_delta;
732 _editor->cursor_group->raise_to_top();
735 if (x_delta != 0 && !_brushing) {
736 show_verbose_cursor_time (_last_frame_position);
739 _last_pointer_time_axis_view += delta_time_axis_view;
740 _last_pointer_layer += delta_layer;
744 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
746 if (_copy && first_move) {
748 /* duplicate the regionview(s) and region(s) */
750 list<DraggingView> new_regionviews;
752 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
754 RegionView* rv = i->view;
755 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
756 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
758 const boost::shared_ptr<const Region> original = rv->region();
759 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
760 region_copy->set_position (original->position());
764 boost::shared_ptr<AudioRegion> audioregion_copy
765 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
767 nrv = new AudioRegionView (*arv, audioregion_copy);
769 boost::shared_ptr<MidiRegion> midiregion_copy
770 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
771 nrv = new MidiRegionView (*mrv, midiregion_copy);
776 nrv->get_canvas_group()->show ();
777 new_regionviews.push_back (DraggingView (nrv, this));
779 /* swap _primary to the copy */
781 if (rv == _primary) {
785 /* ..and deselect the one we copied */
787 rv->set_selected (false);
790 if (!new_regionviews.empty()) {
792 /* reflect the fact that we are dragging the copies */
794 _views = new_regionviews;
796 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
799 sync the canvas to what we think is its current state
800 without it, the canvas seems to
801 "forget" to update properly after the upcoming reparent()
802 ..only if the mouse is in rapid motion at the time of the grab.
803 something to do with regionview creation taking so long?
805 _editor->update_canvas_now();
809 RegionMotionDrag::motion (event, first_move);
813 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
815 if (!movement_occurred) {
820 /* reverse this here so that we have the correct logic to finalize
824 if (Config->get_edit_mode() == Lock) {
825 _x_constrained = !_x_constrained;
828 assert (!_views.empty ());
830 bool const changed_position = (_last_frame_position != _primary->region()->position());
831 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
832 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
834 _editor->update_canvas_now ();
856 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
858 RegionSelection new_views;
859 PlaylistSet modified_playlists;
860 list<RegionView*> views_to_delete;
863 /* all changes were made during motion event handlers */
865 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
869 _editor->commit_reversible_command ();
873 if (_x_constrained) {
874 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
876 _editor->begin_reversible_command (Operations::region_copy);
879 /* insert the regions into their new playlists */
880 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
882 if (i->view->region()->locked()) {
888 if (changed_position && !_x_constrained) {
889 where = i->view->region()->position() - drag_delta;
891 where = i->view->region()->position();
894 RegionView* new_view = insert_region_into_playlist (
895 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
902 new_views.push_back (new_view);
904 /* we don't need the copied RegionView any more */
905 views_to_delete.push_back (i->view);
908 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
909 because when views are deleted they are automagically removed from _views, which messes
912 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
916 /* If we've created new regions either by copying or moving
917 to a new track, we want to replace the old selection with the new ones
920 if (new_views.size() > 0) {
921 _editor->selection->set (new_views);
924 /* write commands for the accumulated diffs for all our modified playlists */
925 add_stateful_diff_commands_for_playlists (modified_playlists);
927 _editor->commit_reversible_command ();
931 RegionMoveDrag::finished_no_copy (
932 bool const changed_position,
933 bool const changed_tracks,
934 framecnt_t const drag_delta
937 RegionSelection new_views;
938 PlaylistSet modified_playlists;
939 PlaylistSet frozen_playlists;
942 /* all changes were made during motion event handlers */
943 _editor->commit_reversible_command ();
947 if (_x_constrained) {
948 _editor->begin_reversible_command (_("fixed time region drag"));
950 _editor->begin_reversible_command (Operations::region_drag);
953 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
955 RegionView* rv = i->view;
957 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
958 layer_t const dest_layer = i->layer;
960 if (rv->region()->locked()) {
967 if (changed_position && !_x_constrained) {
968 where = rv->region()->position() - drag_delta;
970 where = rv->region()->position();
973 if (changed_tracks) {
975 /* insert into new playlist */
977 RegionView* new_view = insert_region_into_playlist (
978 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
986 new_views.push_back (new_view);
988 /* remove from old playlist */
990 /* the region that used to be in the old playlist is not
991 moved to the new one - we use a copy of it. as a result,
992 any existing editor for the region should no longer be
995 rv->hide_region_editor();
996 rv->fake_set_opaque (false);
998 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1002 rv->region()->clear_changes ();
1005 motion on the same track. plonk the previously reparented region
1006 back to its original canvas group (its streamview).
1007 No need to do anything for copies as they are fake regions which will be deleted.
1010 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1011 rv->get_canvas_group()->property_y() = i->initial_y;
1012 rv->get_time_axis_view().reveal_dependent_views (*rv);
1014 /* just change the model */
1016 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1018 if (dest_rtv->view()->layer_display() == Stacked) {
1019 rv->region()->set_layer (dest_layer);
1020 rv->region()->set_pending_explicit_relayer (true);
1023 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1025 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1028 playlist->freeze ();
1031 /* this movement may result in a crossfade being modified, so we need to get undo
1032 data from the playlist as well as the region.
1035 r = modified_playlists.insert (playlist);
1037 playlist->clear_changes ();
1040 rv->region()->set_position (where);
1042 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1045 if (changed_tracks) {
1047 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1048 was selected in all of them, then removing it from a playlist will have removed all
1049 trace of it from _views (i.e. there were N regions selected, we removed 1,
1050 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1051 corresponding regionview, and _views is now empty).
1053 This could have invalidated any and all iterators into _views.
1055 The heuristic we use here is: if the region selection is empty, break out of the loop
1056 here. if the region selection is not empty, then restart the loop because we know that
1057 we must have removed at least the region(view) we've just been working on as well as any
1058 that we processed on previous iterations.
1060 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1061 we can just iterate.
1065 if (_views.empty()) {
1076 /* If we've created new regions either by copying or moving
1077 to a new track, we want to replace the old selection with the new ones
1080 if (new_views.size() > 0) {
1081 _editor->selection->set (new_views);
1084 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1088 /* write commands for the accumulated diffs for all our modified playlists */
1089 add_stateful_diff_commands_for_playlists (modified_playlists);
1091 _editor->commit_reversible_command ();
1094 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1095 * @param region Region to remove.
1096 * @param playlist playlist To remove from.
1097 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1098 * that clear_changes () is only called once per playlist.
1101 RegionMoveDrag::remove_region_from_playlist (
1102 boost::shared_ptr<Region> region,
1103 boost::shared_ptr<Playlist> playlist,
1104 PlaylistSet& modified_playlists
1107 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1110 playlist->clear_changes ();
1113 playlist->remove_region (region);
1117 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1118 * clearing the playlist's diff history first if necessary.
1119 * @param region Region to insert.
1120 * @param dest_rtv Destination RouteTimeAxisView.
1121 * @param dest_layer Destination layer.
1122 * @param where Destination position.
1123 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1124 * that clear_changes () is only called once per playlist.
1125 * @return New RegionView, or 0 if no insert was performed.
1128 RegionMoveDrag::insert_region_into_playlist (
1129 boost::shared_ptr<Region> region,
1130 RouteTimeAxisView* dest_rtv,
1133 PlaylistSet& modified_playlists
1136 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1137 if (!dest_playlist) {
1141 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1142 _new_region_view = 0;
1143 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1145 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1146 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1148 dest_playlist->clear_changes ();
1151 dest_playlist->add_region (region, where);
1153 if (dest_rtv->view()->layer_display() == Stacked) {
1154 region->set_layer (dest_layer);
1155 region->set_pending_explicit_relayer (true);
1160 assert (_new_region_view);
1162 return _new_region_view;
1166 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1168 _new_region_view = rv;
1172 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1174 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1175 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1177 _editor->session()->add_command (c);
1186 RegionMoveDrag::aborted (bool movement_occurred)
1190 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1197 RegionMotionDrag::aborted (movement_occurred);
1202 RegionMotionDrag::aborted (bool)
1204 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1205 RegionView* rv = i->view;
1206 TimeAxisView* tv = &(rv->get_time_axis_view ());
1207 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1209 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1210 rv->get_canvas_group()->property_y() = 0;
1211 rv->get_time_axis_view().reveal_dependent_views (*rv);
1212 rv->fake_set_opaque (false);
1213 rv->move (-_total_x_delta, 0);
1214 rv->set_height (rtv->view()->child_height ());
1217 _editor->update_canvas_now ();
1220 /** @param b true to brush, otherwise false.
1221 * @param c true to make copies of the regions being moved, otherwise false.
1223 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1224 : RegionMotionDrag (e, i, p, v, b),
1227 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1230 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1231 if (rtv && rtv->is_track()) {
1232 speed = rtv->track()->speed ();
1235 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1239 RegionMoveDrag::setup_pointer_frame_offset ()
1241 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1244 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1245 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1247 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1249 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1250 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1252 _primary = v->view()->create_region_view (r, false, false);
1254 _primary->get_canvas_group()->show ();
1255 _primary->set_position (pos, 0);
1256 _views.push_back (DraggingView (_primary, this));
1258 _last_frame_position = pos;
1260 _item = _primary->get_canvas_group ();
1264 RegionInsertDrag::finished (GdkEvent *, bool)
1266 _editor->update_canvas_now ();
1268 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1270 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1271 _primary->get_canvas_group()->property_y() = 0;
1273 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1275 _editor->begin_reversible_command (Operations::insert_region);
1276 playlist->clear_changes ();
1277 playlist->add_region (_primary->region (), _last_frame_position);
1278 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1279 _editor->commit_reversible_command ();
1287 RegionInsertDrag::aborted (bool)
1294 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1295 : RegionMoveDrag (e, i, p, v, false, false)
1297 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1300 struct RegionSelectionByPosition {
1301 bool operator() (RegionView*a, RegionView* b) {
1302 return a->region()->position () < b->region()->position();
1307 RegionSpliceDrag::motion (GdkEvent* event, bool)
1309 /* Which trackview is this ? */
1311 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1312 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1313 layer_t layer = tvp.second;
1315 if (tv && tv->layer_display() == Overlaid) {
1319 /* The region motion is only processed if the pointer is over
1323 if (!tv || !tv->is_track()) {
1324 /* To make sure we hide the verbose canvas cursor when the mouse is
1325 not held over and audiotrack.
1327 _editor->verbose_cursor()->hide ();
1333 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1339 RegionSelection copy (_editor->selection->regions);
1341 RegionSelectionByPosition cmp;
1344 framepos_t const pf = adjusted_current_frame (event);
1346 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1348 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1354 boost::shared_ptr<Playlist> playlist;
1356 if ((playlist = atv->playlist()) == 0) {
1360 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1365 if (pf < (*i)->region()->last_frame() + 1) {
1369 if (pf > (*i)->region()->first_frame()) {
1375 playlist->shuffle ((*i)->region(), dir);
1380 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1382 RegionMoveDrag::finished (event, movement_occurred);
1386 RegionSpliceDrag::aborted (bool)
1391 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1393 _view (dynamic_cast<MidiTimeAxisView*> (v))
1395 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1401 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1405 _view->playlist()->freeze ();
1408 framepos_t const f = adjusted_current_frame (event);
1409 if (f < grab_frame()) {
1410 _region->set_position (f);
1413 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1414 so that if this region is duplicated, its duplicate starts on
1415 a snap point rather than 1 frame after a snap point. Otherwise things get
1416 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1417 place snapped notes at the start of the region.
1420 framecnt_t const len = abs (f - grab_frame () - 1);
1421 _region->set_length (len < 1 ? 1 : len);
1427 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1429 if (!movement_occurred) {
1432 _view->playlist()->thaw ();
1436 _editor->commit_reversible_command ();
1441 RegionCreateDrag::add_region ()
1443 if (_editor->session()) {
1444 const TempoMap& map (_editor->session()->tempo_map());
1445 framecnt_t pos = grab_frame();
1446 const Meter& m = map.meter_at (pos);
1447 /* not that the frame rate used here can be affected by pull up/down which
1450 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1451 _region = _view->add_region (grab_frame(), len, false);
1456 RegionCreateDrag::aborted (bool)
1459 _view->playlist()->thaw ();
1465 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1469 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1473 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1475 Gdk::Cursor* cursor;
1476 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1477 float x_fraction = cnote->mouse_x_fraction ();
1479 if (x_fraction > 0.0 && x_fraction < 0.25) {
1480 cursor = _editor->cursors()->left_side_trim;
1482 cursor = _editor->cursors()->right_side_trim;
1485 Drag::start_grab (event, cursor);
1487 region = &cnote->region_view();
1489 double const region_start = region->get_position_pixels();
1490 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1492 if (grab_x() <= middle_point) {
1493 cursor = _editor->cursors()->left_side_trim;
1496 cursor = _editor->cursors()->right_side_trim;
1500 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1502 if (event->motion.state & Keyboard::PrimaryModifier) {
1508 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1510 if (ms.size() > 1) {
1511 /* has to be relative, may make no sense otherwise */
1515 /* select this note; if it is already selected, preserve the existing selection,
1516 otherwise make this note the only one selected.
1518 region->note_selected (cnote, cnote->selected ());
1520 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1521 MidiRegionSelection::iterator next;
1524 (*r)->begin_resizing (at_front);
1530 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1532 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1533 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1534 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1539 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1541 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1542 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1543 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1548 NoteResizeDrag::aborted (bool)
1553 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1556 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1560 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1566 RegionGainDrag::finished (GdkEvent *, bool)
1572 RegionGainDrag::aborted (bool)
1577 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1578 : RegionDrag (e, i, p, v)
1580 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1584 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1587 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1588 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1590 if (tv && tv->is_track()) {
1591 speed = tv->track()->speed();
1594 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1595 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1596 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1598 framepos_t const pf = adjusted_current_frame (event);
1600 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1601 /* Move the contents of the region around without changing the region bounds */
1602 _operation = ContentsTrim;
1603 Drag::start_grab (event, _editor->cursors()->trimmer);
1605 /* These will get overridden for a point trim.*/
1606 if (pf < (region_start + region_length/2)) {
1607 /* closer to front */
1608 _operation = StartTrim;
1609 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1612 _operation = EndTrim;
1613 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1617 switch (_operation) {
1619 show_verbose_cursor_time (region_start);
1620 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1621 i->view->trim_front_starting ();
1625 show_verbose_cursor_time (region_end);
1628 show_verbose_cursor_time (pf);
1632 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1633 i->view->region()->suspend_property_changes ();
1638 TrimDrag::motion (GdkEvent* event, bool first_move)
1640 RegionView* rv = _primary;
1643 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1644 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1645 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1647 if (tv && tv->is_track()) {
1648 speed = tv->track()->speed();
1651 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1657 switch (_operation) {
1659 trim_type = "Region start trim";
1662 trim_type = "Region end trim";
1665 trim_type = "Region content trim";
1669 _editor->begin_reversible_command (trim_type);
1671 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1672 RegionView* rv = i->view;
1673 rv->fake_set_opaque (false);
1674 rv->enable_display (false);
1675 rv->region()->playlist()->clear_owned_changes ();
1677 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1680 arv->temporarily_hide_envelope ();
1683 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1684 insert_result = _editor->motion_frozen_playlists.insert (pl);
1686 if (insert_result.second) {
1692 bool non_overlap_trim = false;
1694 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1695 non_overlap_trim = true;
1698 switch (_operation) {
1700 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1701 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1706 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1707 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1713 bool swap_direction = false;
1715 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1716 swap_direction = true;
1719 framecnt_t frame_delta = 0;
1721 bool left_direction = false;
1722 if (last_pointer_frame() > adjusted_current_frame(event)) {
1723 left_direction = true;
1726 if (left_direction) {
1727 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1729 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1732 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1733 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1739 switch (_operation) {
1741 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1744 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1747 show_verbose_cursor_time (adjusted_current_frame (event));
1754 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1756 if (movement_occurred) {
1757 motion (event, false);
1759 /* This must happen before the region's StatefulDiffCommand is created, as it may
1760 `correct' (ahem) the region's _start from being negative to being zero. It
1761 needs to be zero in the undo record.
1763 if (_operation == StartTrim) {
1764 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1765 i->view->trim_front_ending ();
1769 if (!_editor->selection->selected (_primary)) {
1770 _primary->thaw_after_trim ();
1773 set<boost::shared_ptr<Playlist> > diffed_playlists;
1775 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1776 i->view->thaw_after_trim ();
1777 i->view->enable_display (true);
1778 i->view->fake_set_opaque (true);
1780 /* Trimming one region may affect others on the playlist, so we need
1781 to get undo Commands from the whole playlist rather than just the
1782 region. Use diffed_playlists to make sure we don't diff a given
1783 playlist more than once.
1785 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1786 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1787 vector<Command*> cmds;
1789 _editor->session()->add_commands (cmds);
1790 diffed_playlists.insert (p);
1794 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1798 _editor->motion_frozen_playlists.clear ();
1799 _editor->commit_reversible_command();
1802 /* no mouse movement */
1803 _editor->point_trim (event, adjusted_current_frame (event));
1806 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1807 if (_operation == StartTrim) {
1808 i->view->trim_front_ending ();
1811 i->view->region()->resume_property_changes ();
1816 TrimDrag::aborted (bool movement_occurred)
1818 /* Our motion method is changing model state, so use the Undo system
1819 to cancel. Perhaps not ideal, as this will leave an Undo point
1820 behind which may be slightly odd from the user's point of view.
1825 if (movement_occurred) {
1829 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1830 i->view->region()->resume_property_changes ();
1835 TrimDrag::setup_pointer_frame_offset ()
1837 list<DraggingView>::iterator i = _views.begin ();
1838 while (i != _views.end() && i->view != _primary) {
1842 if (i == _views.end()) {
1846 switch (_operation) {
1848 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1851 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1858 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1862 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1864 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1869 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1872 // create a dummy marker for visual representation of moving the copy.
1873 // The actual copying is not done before we reach the finish callback.
1875 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1877 MeterMarker* new_marker = new MeterMarker (
1879 *_editor->meter_group,
1880 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1882 *new MeterSection (_marker->meter())
1885 _item = &new_marker->the_item ();
1886 _marker = new_marker;
1890 MetricSection& section (_marker->meter());
1892 if (!section.movable()) {
1898 Drag::start_grab (event, cursor);
1900 show_verbose_cursor_time (adjusted_current_frame(event));
1904 MeterMarkerDrag::setup_pointer_frame_offset ()
1906 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1910 MeterMarkerDrag::motion (GdkEvent* event, bool)
1912 framepos_t const pf = adjusted_current_frame (event);
1914 _marker->set_position (pf);
1916 show_verbose_cursor_time (pf);
1920 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1922 if (!movement_occurred) {
1926 motion (event, false);
1928 Timecode::BBT_Time when;
1930 TempoMap& map (_editor->session()->tempo_map());
1931 map.bbt_time (last_pointer_frame(), when);
1933 if (_copy == true) {
1934 _editor->begin_reversible_command (_("copy meter mark"));
1935 XMLNode &before = map.get_state();
1936 map.add_meter (_marker->meter(), when);
1937 XMLNode &after = map.get_state();
1938 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1939 _editor->commit_reversible_command ();
1941 // delete the dummy marker we used for visual representation of copying.
1942 // a new visual marker will show up automatically.
1945 _editor->begin_reversible_command (_("move meter mark"));
1946 XMLNode &before = map.get_state();
1947 map.move_meter (_marker->meter(), when);
1948 XMLNode &after = map.get_state();
1949 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1950 _editor->commit_reversible_command ();
1955 MeterMarkerDrag::aborted (bool)
1957 _marker->set_position (_marker->meter().frame ());
1960 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1964 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1966 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1971 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1975 // create a dummy marker for visual representation of moving the copy.
1976 // The actual copying is not done before we reach the finish callback.
1978 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1980 TempoMarker* new_marker = new TempoMarker (
1982 *_editor->tempo_group,
1983 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1985 *new TempoSection (_marker->tempo())
1988 _item = &new_marker->the_item ();
1989 _marker = new_marker;
1993 Drag::start_grab (event, cursor);
1995 show_verbose_cursor_time (adjusted_current_frame (event));
1999 TempoMarkerDrag::setup_pointer_frame_offset ()
2001 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2005 TempoMarkerDrag::motion (GdkEvent* event, bool)
2007 framepos_t const pf = adjusted_current_frame (event);
2008 _marker->set_position (pf);
2009 show_verbose_cursor_time (pf);
2013 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2015 if (!movement_occurred) {
2019 motion (event, false);
2021 Timecode::BBT_Time when;
2023 TempoMap& map (_editor->session()->tempo_map());
2024 map.bbt_time (last_pointer_frame(), when);
2026 if (_copy == true) {
2027 _editor->begin_reversible_command (_("copy tempo mark"));
2028 XMLNode &before = map.get_state();
2029 map.add_tempo (_marker->tempo(), when);
2030 XMLNode &after = map.get_state();
2031 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2032 _editor->commit_reversible_command ();
2034 // delete the dummy marker we used for visual representation of copying.
2035 // a new visual marker will show up automatically.
2038 _editor->begin_reversible_command (_("move tempo mark"));
2039 XMLNode &before = map.get_state();
2040 map.move_tempo (_marker->tempo(), when);
2041 XMLNode &after = map.get_state();
2042 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2043 _editor->commit_reversible_command ();
2048 TempoMarkerDrag::aborted (bool)
2050 _marker->set_position (_marker->tempo().frame());
2053 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2057 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2060 /** Do all the things we do when dragging the playhead to make it look as though
2061 * we have located, without actually doing the locate (because that would cause
2062 * the diskstream buffers to be refilled, which is too slow).
2065 CursorDrag::fake_locate (framepos_t t)
2067 _editor->playhead_cursor->set_position (t);
2069 Session* s = _editor->session ();
2070 if (s->timecode_transmission_suspended ()) {
2071 framepos_t const f = _editor->playhead_cursor->current_frame;
2072 s->send_mmc_locate (f);
2073 s->send_full_time_code (f);
2076 show_verbose_cursor_time (t);
2077 _editor->UpdateAllTransportClocks (t);
2081 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2083 Drag::start_grab (event, c);
2085 _grab_zoom = _editor->frames_per_unit;
2087 framepos_t where = _editor->event_frame (event, 0, 0);
2088 _editor->snap_to_with_modifier (where, event);
2090 _editor->_dragging_playhead = true;
2092 Session* s = _editor->session ();
2095 if (_was_rolling && _stop) {
2099 if (s->is_auditioning()) {
2100 s->cancel_audition ();
2103 s->request_suspend_timecode_transmission ();
2104 while (!s->timecode_transmission_suspended ()) {
2105 /* twiddle our thumbs */
2109 fake_locate (where);
2113 CursorDrag::motion (GdkEvent* event, bool)
2115 if (_drags->current_pointer_y() != last_pointer_y()) {
2117 /* zoom when we move the pointer up and down */
2119 /* y range to operate over (pixels) */
2120 double const y_range = 256;
2121 /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */
2122 double const scale_range = 4;
2123 /* dead zone around the grab point in which to do no zooming (pixels) */
2124 double const dead_zone = 16;
2127 double dy = _drags->current_pointer_y() - grab_y();
2129 if (dy < -dead_zone || dy > dead_zone) {
2130 /* we are outside the dead zone; remove it from our calculation */
2137 /* get a number from -1 to 1 as dy ranges from -y_range to y_range */
2138 double udy = max (min (dy / y_range, 1.0), -1.0);
2140 /* and zoom, using playhead focus temporarily */
2141 Editing::ZoomFocus const zf = _editor->get_zoom_focus ();
2142 _editor->set_zoom_focus (Editing::ZoomFocusPlayhead);
2143 _editor->temporal_zoom (_grab_zoom * pow (scale_range, -udy));
2144 _editor->set_zoom_focus (zf);
2148 framepos_t const adjusted_frame = adjusted_current_frame (event);
2149 if (adjusted_frame != last_pointer_frame()) {
2150 fake_locate (adjusted_frame);
2152 _editor->update_canvas_now ();
2158 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2160 _editor->_dragging_playhead = false;
2162 if (!movement_occurred && _stop) {
2166 motion (event, false);
2168 Session* s = _editor->session ();
2170 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2171 _editor->_pending_locate_request = true;
2172 s->request_resume_timecode_transmission ();
2177 CursorDrag::aborted (bool)
2179 if (_editor->_dragging_playhead) {
2180 _editor->session()->request_resume_timecode_transmission ();
2181 _editor->_dragging_playhead = false;
2184 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2187 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2188 : RegionDrag (e, i, p, v)
2190 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2194 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2196 Drag::start_grab (event, cursor);
2198 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2199 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2201 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2203 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2207 FadeInDrag::setup_pointer_frame_offset ()
2209 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2210 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2211 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2215 FadeInDrag::motion (GdkEvent* event, bool)
2217 framecnt_t fade_length;
2219 framepos_t const pos = adjusted_current_frame (event);
2221 boost::shared_ptr<Region> region = _primary->region ();
2223 if (pos < (region->position() + 64)) {
2224 fade_length = 64; // this should be a minimum defined somewhere
2225 } else if (pos > region->last_frame()) {
2226 fade_length = region->length();
2228 fade_length = pos - region->position();
2231 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2233 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2239 tmp->reset_fade_in_shape_width (fade_length);
2240 tmp->show_fade_line((framecnt_t) fade_length);
2243 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2247 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2249 if (!movement_occurred) {
2253 framecnt_t fade_length;
2255 framepos_t const pos = adjusted_current_frame (event);
2257 boost::shared_ptr<Region> region = _primary->region ();
2259 if (pos < (region->position() + 64)) {
2260 fade_length = 64; // this should be a minimum defined somewhere
2261 } else if (pos > region->last_frame()) {
2262 fade_length = region->length();
2264 fade_length = pos - region->position();
2267 _editor->begin_reversible_command (_("change fade in length"));
2269 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2271 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2277 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2278 XMLNode &before = alist->get_state();
2280 tmp->audio_region()->set_fade_in_length (fade_length);
2281 tmp->audio_region()->set_fade_in_active (true);
2282 tmp->hide_fade_line();
2284 XMLNode &after = alist->get_state();
2285 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2288 _editor->commit_reversible_command ();
2292 FadeInDrag::aborted (bool)
2294 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2295 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2301 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2302 tmp->hide_fade_line();
2306 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2307 : RegionDrag (e, i, p, v)
2309 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2313 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2315 Drag::start_grab (event, cursor);
2317 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2318 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2320 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2322 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2326 FadeOutDrag::setup_pointer_frame_offset ()
2328 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2329 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2330 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2334 FadeOutDrag::motion (GdkEvent* event, bool)
2336 framecnt_t fade_length;
2338 framepos_t const pos = adjusted_current_frame (event);
2340 boost::shared_ptr<Region> region = _primary->region ();
2342 if (pos > (region->last_frame() - 64)) {
2343 fade_length = 64; // this should really be a minimum fade defined somewhere
2345 else if (pos < region->position()) {
2346 fade_length = region->length();
2349 fade_length = region->last_frame() - pos;
2352 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2354 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2360 tmp->reset_fade_out_shape_width (fade_length);
2361 tmp->show_fade_line(region->length() - fade_length);
2364 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2368 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2370 if (!movement_occurred) {
2374 framecnt_t fade_length;
2376 framepos_t const pos = adjusted_current_frame (event);
2378 boost::shared_ptr<Region> region = _primary->region ();
2380 if (pos > (region->last_frame() - 64)) {
2381 fade_length = 64; // this should really be a minimum fade defined somewhere
2383 else if (pos < region->position()) {
2384 fade_length = region->length();
2387 fade_length = region->last_frame() - pos;
2390 _editor->begin_reversible_command (_("change fade out length"));
2392 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2394 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2400 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2401 XMLNode &before = alist->get_state();
2403 tmp->audio_region()->set_fade_out_length (fade_length);
2404 tmp->audio_region()->set_fade_out_active (true);
2405 tmp->hide_fade_line();
2407 XMLNode &after = alist->get_state();
2408 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2411 _editor->commit_reversible_command ();
2415 FadeOutDrag::aborted (bool)
2417 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2418 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2424 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2425 tmp->hide_fade_line();
2429 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2432 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2434 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2437 _points.push_back (Gnome::Art::Point (0, 0));
2438 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2441 MarkerDrag::~MarkerDrag ()
2443 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2449 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2451 Drag::start_grab (event, cursor);
2455 Location *location = _editor->find_location_from_marker (_marker, is_start);
2456 _editor->_dragging_edit_point = true;
2458 update_item (location);
2460 // _drag_line->show();
2461 // _line->raise_to_top();
2464 show_verbose_cursor_time (location->start());
2466 show_verbose_cursor_time (location->end());
2469 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2472 case Selection::Toggle:
2473 _editor->selection->toggle (_marker);
2475 case Selection::Set:
2476 if (!_editor->selection->selected (_marker)) {
2477 _editor->selection->set (_marker);
2480 case Selection::Extend:
2482 Locations::LocationList ll;
2483 list<Marker*> to_add;
2485 _editor->selection->markers.range (s, e);
2486 s = min (_marker->position(), s);
2487 e = max (_marker->position(), e);
2490 if (e < max_framepos) {
2493 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2494 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2495 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2498 to_add.push_back (lm->start);
2501 to_add.push_back (lm->end);
2505 if (!to_add.empty()) {
2506 _editor->selection->add (to_add);
2510 case Selection::Add:
2511 _editor->selection->add (_marker);
2515 /* Set up copies for us to manipulate during the drag */
2517 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2518 Location* l = _editor->find_location_from_marker (*i, is_start);
2519 _copied_locations.push_back (new Location (*l));
2524 MarkerDrag::setup_pointer_frame_offset ()
2527 Location *location = _editor->find_location_from_marker (_marker, is_start);
2528 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2532 MarkerDrag::motion (GdkEvent* event, bool)
2534 framecnt_t f_delta = 0;
2536 bool move_both = false;
2538 Location *real_location;
2539 Location *copy_location = 0;
2541 framepos_t const newframe = adjusted_current_frame (event);
2543 framepos_t next = newframe;
2545 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2549 MarkerSelection::iterator i;
2550 list<Location*>::iterator x;
2552 /* find the marker we're dragging, and compute the delta */
2554 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2555 x != _copied_locations.end() && i != _editor->selection->markers.end();
2561 if (marker == _marker) {
2563 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2568 if (real_location->is_mark()) {
2569 f_delta = newframe - copy_location->start();
2573 switch (marker->type()) {
2574 case Marker::SessionStart:
2575 case Marker::RangeStart:
2576 case Marker::LoopStart:
2577 case Marker::PunchIn:
2578 f_delta = newframe - copy_location->start();
2581 case Marker::SessionEnd:
2582 case Marker::RangeEnd:
2583 case Marker::LoopEnd:
2584 case Marker::PunchOut:
2585 f_delta = newframe - copy_location->end();
2588 /* what kind of marker is this ? */
2596 if (i == _editor->selection->markers.end()) {
2597 /* hmm, impossible - we didn't find the dragged marker */
2601 /* now move them all */
2603 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2604 x != _copied_locations.end() && i != _editor->selection->markers.end();
2610 /* call this to find out if its the start or end */
2612 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2616 if (real_location->locked()) {
2620 if (copy_location->is_mark()) {
2624 copy_location->set_start (copy_location->start() + f_delta);
2628 framepos_t new_start = copy_location->start() + f_delta;
2629 framepos_t new_end = copy_location->end() + f_delta;
2631 if (is_start) { // start-of-range marker
2634 copy_location->set_start (new_start);
2635 copy_location->set_end (new_end);
2636 } else if (new_start < copy_location->end()) {
2637 copy_location->set_start (new_start);
2638 } else if (newframe > 0) {
2639 _editor->snap_to (next, 1, true);
2640 copy_location->set_end (next);
2641 copy_location->set_start (newframe);
2644 } else { // end marker
2647 copy_location->set_end (new_end);
2648 copy_location->set_start (new_start);
2649 } else if (new_end > copy_location->start()) {
2650 copy_location->set_end (new_end);
2651 } else if (newframe > 0) {
2652 _editor->snap_to (next, -1, true);
2653 copy_location->set_start (next);
2654 copy_location->set_end (newframe);
2659 update_item (copy_location);
2661 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2664 lm->set_position (copy_location->start(), copy_location->end());
2668 assert (!_copied_locations.empty());
2670 show_verbose_cursor_time (newframe);
2673 _editor->update_canvas_now ();
2678 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2680 if (!movement_occurred) {
2682 /* just a click, do nothing but finish
2683 off the selection process
2686 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2689 case Selection::Set:
2690 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2691 _editor->selection->set (_marker);
2695 case Selection::Toggle:
2696 case Selection::Extend:
2697 case Selection::Add:
2704 _editor->_dragging_edit_point = false;
2706 _editor->begin_reversible_command ( _("move marker") );
2707 XMLNode &before = _editor->session()->locations()->get_state();
2709 MarkerSelection::iterator i;
2710 list<Location*>::iterator x;
2713 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2714 x != _copied_locations.end() && i != _editor->selection->markers.end();
2717 Location * location = _editor->find_location_from_marker (*i, is_start);
2721 if (location->locked()) {
2725 if (location->is_mark()) {
2726 location->set_start ((*x)->start());
2728 location->set ((*x)->start(), (*x)->end());
2733 XMLNode &after = _editor->session()->locations()->get_state();
2734 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2735 _editor->commit_reversible_command ();
2739 MarkerDrag::aborted (bool)
2745 MarkerDrag::update_item (Location* location)
2750 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2752 _cumulative_x_drag (0),
2753 _cumulative_y_drag (0)
2755 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2757 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2763 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2765 Drag::start_grab (event, _editor->cursors()->fader);
2767 // start the grab at the center of the control point so
2768 // the point doesn't 'jump' to the mouse after the first drag
2769 _fixed_grab_x = _point->get_x();
2770 _fixed_grab_y = _point->get_y();
2772 float const fraction = 1 - (_point->get_y() / _point->line().height());
2774 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2776 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2777 event->button.x + 10, event->button.y + 10);
2779 _editor->verbose_cursor()->show ();
2783 ControlPointDrag::motion (GdkEvent* event, bool)
2785 double dx = _drags->current_pointer_x() - last_pointer_x();
2786 double dy = _drags->current_pointer_y() - last_pointer_y();
2788 if (event->button.state & Keyboard::SecondaryModifier) {
2793 /* coordinate in pixels relative to the start of the region (for region-based automation)
2794 or track (for track-based automation) */
2795 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2796 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2798 // calculate zero crossing point. back off by .01 to stay on the
2799 // positive side of zero
2800 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2802 // make sure we hit zero when passing through
2803 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2807 if (_x_constrained) {
2810 if (_y_constrained) {
2814 _cumulative_x_drag = cx - _fixed_grab_x;
2815 _cumulative_y_drag = cy - _fixed_grab_y;
2819 cy = min ((double) _point->line().height(), cy);
2821 framepos_t cx_frames = _editor->unit_to_frame (cx);
2823 if (!_x_constrained) {
2824 _editor->snap_to_with_modifier (cx_frames, event);
2827 cx_frames = min (cx_frames, _point->line().maximum_time());
2829 float const fraction = 1.0 - (cy / _point->line().height());
2831 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2833 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2835 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2839 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2841 if (!movement_occurred) {
2845 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2846 _editor->reset_point_selection ();
2850 motion (event, false);
2853 _point->line().end_drag ();
2854 _editor->session()->commit_reversible_command ();
2858 ControlPointDrag::aborted (bool)
2860 _point->line().reset ();
2864 ControlPointDrag::active (Editing::MouseMode m)
2866 if (m == Editing::MouseGain) {
2867 /* always active in mouse gain */
2871 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2872 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2875 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2878 _cumulative_y_drag (0)
2880 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2884 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2886 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2889 _item = &_line->grab_item ();
2891 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2892 origin, and ditto for y.
2895 double cx = event->button.x;
2896 double cy = event->button.y;
2898 _line->parent_group().w2i (cx, cy);
2900 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2905 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2906 /* no adjacent points */
2910 Drag::start_grab (event, _editor->cursors()->fader);
2912 /* store grab start in parent frame */
2917 double fraction = 1.0 - (cy / _line->height());
2919 _line->start_drag_line (before, after, fraction);
2921 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2922 event->button.x + 10, event->button.y + 10);
2924 _editor->verbose_cursor()->show ();
2928 LineDrag::motion (GdkEvent* event, bool)
2930 double dy = _drags->current_pointer_y() - last_pointer_y();
2932 if (event->button.state & Keyboard::SecondaryModifier) {
2936 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2938 _cumulative_y_drag = cy - _fixed_grab_y;
2941 cy = min ((double) _line->height(), cy);
2943 double const fraction = 1.0 - (cy / _line->height());
2947 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2953 /* we are ignoring x position for this drag, so we can just pass in anything */
2954 _line->drag_motion (0, fraction, true, push);
2956 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2960 LineDrag::finished (GdkEvent* event, bool)
2962 motion (event, false);
2964 _editor->session()->commit_reversible_command ();
2968 LineDrag::aborted (bool)
2973 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2976 _cumulative_x_drag (0)
2978 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2982 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2984 Drag::start_grab (event);
2986 _line = reinterpret_cast<Line*> (_item);
2989 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2991 double cx = event->button.x;
2992 double cy = event->button.y;
2994 _item->property_parent().get_value()->w2i(cx, cy);
2996 /* store grab start in parent frame */
2997 _region_view_grab_x = cx;
2999 _before = *(float*) _item->get_data ("position");
3001 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3003 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3007 FeatureLineDrag::motion (GdkEvent*, bool)
3009 double dx = _drags->current_pointer_x() - last_pointer_x();
3011 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3013 _cumulative_x_drag += dx;
3015 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3024 ArdourCanvas::Points points;
3026 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3028 _line->get_bounds(x1, y2, x2, y2);
3030 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3031 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3033 _line->property_points() = points;
3035 float *pos = new float;
3038 _line->set_data ("position", pos);
3044 FeatureLineDrag::finished (GdkEvent*, bool)
3046 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3047 _arv->update_transient(_before, _before);
3051 FeatureLineDrag::aborted (bool)
3056 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3059 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3063 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3065 Drag::start_grab (event);
3066 show_verbose_cursor_time (adjusted_current_frame (event));
3070 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3077 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3079 framepos_t grab = grab_frame ();
3080 if (Config->get_rubberbanding_snaps_to_grid ()) {
3081 _editor->snap_to_with_modifier (grab, event);
3084 /* base start and end on initial click position */
3094 if (_drags->current_pointer_y() < grab_y()) {
3095 y1 = _drags->current_pointer_y();
3098 y2 = _drags->current_pointer_y();
3103 if (start != end || y1 != y2) {
3105 double x1 = _editor->frame_to_pixel (start);
3106 double x2 = _editor->frame_to_pixel (end);
3108 _editor->rubberband_rect->property_x1() = x1;
3109 _editor->rubberband_rect->property_y1() = y1;
3110 _editor->rubberband_rect->property_x2() = x2;
3111 _editor->rubberband_rect->property_y2() = y2;
3113 _editor->rubberband_rect->show();
3114 _editor->rubberband_rect->raise_to_top();
3116 show_verbose_cursor_time (pf);
3121 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3123 if (movement_occurred) {
3125 motion (event, false);
3128 if (_drags->current_pointer_y() < grab_y()) {
3129 y1 = _drags->current_pointer_y();
3132 y2 = _drags->current_pointer_y();
3137 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3139 _editor->begin_reversible_command (_("rubberband selection"));
3141 if (grab_frame() < last_pointer_frame()) {
3142 _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3144 _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3147 _editor->commit_reversible_command ();
3150 if (!getenv("ARDOUR_SAE")) {
3151 _editor->selection->clear_tracks();
3153 _editor->selection->clear_regions();
3154 _editor->selection->clear_points ();
3155 _editor->selection->clear_lines ();
3158 _editor->rubberband_rect->hide();
3162 RubberbandSelectDrag::aborted (bool)
3164 _editor->rubberband_rect->hide ();
3167 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3168 : RegionDrag (e, i, p, v)
3170 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3174 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3176 Drag::start_grab (event, cursor);
3178 show_verbose_cursor_time (adjusted_current_frame (event));
3182 TimeFXDrag::motion (GdkEvent* event, bool)
3184 RegionView* rv = _primary;
3186 framepos_t const pf = adjusted_current_frame (event);
3188 if (pf > rv->region()->position()) {
3189 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3192 show_verbose_cursor_time (pf);
3196 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3198 _primary->get_time_axis_view().hide_timestretch ();
3200 if (!movement_occurred) {
3204 if (last_pointer_frame() < _primary->region()->position()) {
3205 /* backwards drag of the left edge - not usable */
3209 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3211 float percentage = (double) newlen / (double) _primary->region()->length();
3213 #ifndef USE_RUBBERBAND
3214 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3215 if (_primary->region()->data_type() == DataType::AUDIO) {
3216 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3220 // XXX how do timeFX on multiple regions ?
3225 if (_editor->time_stretch (rs, percentage) == -1) {
3226 error << _("An error occurred while executing time stretch operation") << endmsg;
3231 TimeFXDrag::aborted (bool)
3233 _primary->get_time_axis_view().hide_timestretch ();
3236 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3239 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3243 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3245 Drag::start_grab (event);
3249 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3251 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3255 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3257 if (movement_occurred && _editor->session()) {
3258 /* make sure we stop */
3259 _editor->session()->request_transport_speed (0.0);
3264 ScrubDrag::aborted (bool)
3269 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3273 , _original_pointer_time_axis (-1)
3274 , _last_pointer_time_axis (-1)
3276 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3280 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3282 if (_editor->session() == 0) {
3286 Gdk::Cursor* cursor = 0;
3288 switch (_operation) {
3289 case CreateSelection:
3290 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3295 cursor = _editor->cursors()->selector;
3296 Drag::start_grab (event, cursor);
3299 case SelectionStartTrim:
3300 if (_editor->clicked_axisview) {
3301 _editor->clicked_axisview->order_selection_trims (_item, true);
3303 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3306 case SelectionEndTrim:
3307 if (_editor->clicked_axisview) {
3308 _editor->clicked_axisview->order_selection_trims (_item, false);
3310 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3314 Drag::start_grab (event, cursor);
3318 if (_operation == SelectionMove) {
3319 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3321 show_verbose_cursor_time (adjusted_current_frame (event));
3324 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3328 SelectionDrag::setup_pointer_frame_offset ()
3330 switch (_operation) {
3331 case CreateSelection:
3332 _pointer_frame_offset = 0;
3335 case SelectionStartTrim:
3337 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3340 case SelectionEndTrim:
3341 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3347 SelectionDrag::motion (GdkEvent* event, bool first_move)
3349 framepos_t start = 0;
3353 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3354 if (pending_time_axis.first == 0) {
3358 framepos_t const pending_position = adjusted_current_frame (event);
3360 /* only alter selection if things have changed */
3362 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3366 switch (_operation) {
3367 case CreateSelection:
3369 framepos_t grab = grab_frame ();
3372 _editor->snap_to (grab);
3375 if (pending_position < grab_frame()) {
3376 start = pending_position;
3379 end = pending_position;
3383 /* first drag: Either add to the selection
3384 or create a new selection
3390 /* adding to the selection */
3391 _editor->set_selected_track_as_side_effect (Selection::Add);
3392 //_editor->selection->add (_editor->clicked_axisview);
3393 _editor->clicked_selection = _editor->selection->add (start, end);
3398 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3399 //_editor->selection->set (_editor->clicked_axisview);
3400 _editor->set_selected_track_as_side_effect (Selection::Set);
3403 _editor->clicked_selection = _editor->selection->set (start, end);
3407 /* select the track that we're in */
3408 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3409 // _editor->set_selected_track_as_side_effect (Selection::Add);
3410 _editor->selection->add (pending_time_axis.first);
3411 _added_time_axes.push_back (pending_time_axis.first);
3414 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3415 tracks that we selected in the first place.
3418 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3419 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3421 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3422 while (i != _added_time_axes.end()) {
3424 list<TimeAxisView*>::iterator tmp = i;
3427 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3428 _editor->selection->remove (*i);
3429 _added_time_axes.remove (*i);
3438 case SelectionStartTrim:
3440 start = _editor->selection->time[_editor->clicked_selection].start;
3441 end = _editor->selection->time[_editor->clicked_selection].end;
3443 if (pending_position > end) {
3446 start = pending_position;
3450 case SelectionEndTrim:
3452 start = _editor->selection->time[_editor->clicked_selection].start;
3453 end = _editor->selection->time[_editor->clicked_selection].end;
3455 if (pending_position < start) {
3458 end = pending_position;
3465 start = _editor->selection->time[_editor->clicked_selection].start;
3466 end = _editor->selection->time[_editor->clicked_selection].end;
3468 length = end - start;
3470 start = pending_position;
3471 _editor->snap_to (start);
3473 end = start + length;
3478 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3479 _editor->start_canvas_autoscroll (1, 0);
3483 _editor->selection->replace (_editor->clicked_selection, start, end);
3486 if (_operation == SelectionMove) {
3487 show_verbose_cursor_time(start);
3489 show_verbose_cursor_time(pending_position);
3494 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3496 Session* s = _editor->session();
3498 if (movement_occurred) {
3499 motion (event, false);
3500 /* XXX this is not object-oriented programming at all. ick */
3501 if (_editor->selection->time.consolidate()) {
3502 _editor->selection->TimeChanged ();
3505 /* XXX what if its a music time selection? */
3506 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3507 s->request_play_range (&_editor->selection->time, true);
3512 /* just a click, no pointer movement.*/
3514 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3515 _editor->selection->clear_time();
3518 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3519 _editor->selection->set (_editor->clicked_axisview);
3522 if (s && s->get_play_range () && s->transport_rolling()) {
3523 s->request_stop (false, false);
3528 _editor->stop_canvas_autoscroll ();
3532 SelectionDrag::aborted (bool)
3537 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3542 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3544 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3545 physical_screen_height (_editor->get_window()));
3546 _drag_rect->hide ();
3548 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3549 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3553 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3555 if (_editor->session() == 0) {
3559 Gdk::Cursor* cursor = 0;
3561 if (!_editor->temp_location) {
3562 _editor->temp_location = new Location (*_editor->session());
3565 switch (_operation) {
3566 case CreateRangeMarker:
3567 case CreateTransportMarker:
3568 case CreateCDMarker:
3570 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3575 cursor = _editor->cursors()->selector;
3579 Drag::start_grab (event, cursor);
3581 show_verbose_cursor_time (adjusted_current_frame (event));
3585 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3587 framepos_t start = 0;
3589 ArdourCanvas::SimpleRect *crect;
3591 switch (_operation) {
3592 case CreateRangeMarker:
3593 crect = _editor->range_bar_drag_rect;
3595 case CreateTransportMarker:
3596 crect = _editor->transport_bar_drag_rect;
3598 case CreateCDMarker:
3599 crect = _editor->cd_marker_bar_drag_rect;
3602 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3607 framepos_t const pf = adjusted_current_frame (event);
3609 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3610 framepos_t grab = grab_frame ();
3611 _editor->snap_to (grab);
3613 if (pf < grab_frame()) {
3621 /* first drag: Either add to the selection
3622 or create a new selection.
3627 _editor->temp_location->set (start, end);
3631 update_item (_editor->temp_location);
3633 //_drag_rect->raise_to_top();
3638 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3639 _editor->start_canvas_autoscroll (1, 0);
3643 _editor->temp_location->set (start, end);
3645 double x1 = _editor->frame_to_pixel (start);
3646 double x2 = _editor->frame_to_pixel (end);
3647 crect->property_x1() = x1;
3648 crect->property_x2() = x2;
3650 update_item (_editor->temp_location);
3653 show_verbose_cursor_time (pf);
3658 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3660 Location * newloc = 0;
3664 if (movement_occurred) {
3665 motion (event, false);
3668 switch (_operation) {
3669 case CreateRangeMarker:
3670 case CreateCDMarker:
3672 _editor->begin_reversible_command (_("new range marker"));
3673 XMLNode &before = _editor->session()->locations()->get_state();
3674 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3675 if (_operation == CreateCDMarker) {
3676 flags = Location::IsRangeMarker | Location::IsCDMarker;
3677 _editor->cd_marker_bar_drag_rect->hide();
3680 flags = Location::IsRangeMarker;
3681 _editor->range_bar_drag_rect->hide();
3683 newloc = new Location (
3684 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3687 _editor->session()->locations()->add (newloc, true);
3688 XMLNode &after = _editor->session()->locations()->get_state();
3689 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3690 _editor->commit_reversible_command ();
3694 case CreateTransportMarker:
3695 // popup menu to pick loop or punch
3696 _editor->new_transport_marker_context_menu (&event->button, _item);
3700 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3702 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3707 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3709 if (end == max_framepos) {
3710 end = _editor->session()->current_end_frame ();
3713 if (start == max_framepos) {
3714 start = _editor->session()->current_start_frame ();
3717 switch (_editor->mouse_mode) {
3719 /* find the two markers on either side and then make the selection from it */
3720 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3724 /* find the two markers on either side of the click and make the range out of it */
3725 _editor->selection->set (start, end);
3734 _editor->stop_canvas_autoscroll ();
3738 RangeMarkerBarDrag::aborted (bool)
3744 RangeMarkerBarDrag::update_item (Location* location)
3746 double const x1 = _editor->frame_to_pixel (location->start());
3747 double const x2 = _editor->frame_to_pixel (location->end());
3749 _drag_rect->property_x1() = x1;
3750 _drag_rect->property_x2() = x2;
3753 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3757 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3761 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3763 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3764 Drag::start_grab (event, _editor->cursors()->zoom_out);
3767 Drag::start_grab (event, _editor->cursors()->zoom_in);
3771 show_verbose_cursor_time (adjusted_current_frame (event));
3775 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3780 framepos_t const pf = adjusted_current_frame (event);
3782 framepos_t grab = grab_frame ();
3783 _editor->snap_to_with_modifier (grab, event);
3785 /* base start and end on initial click position */
3797 _editor->zoom_rect->show();
3798 _editor->zoom_rect->raise_to_top();
3801 _editor->reposition_zoom_rect(start, end);
3803 show_verbose_cursor_time (pf);
3808 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3810 if (movement_occurred) {
3811 motion (event, false);
3813 if (grab_frame() < last_pointer_frame()) {
3814 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3816 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3819 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3820 _editor->tav_zoom_step (_zoom_out);
3822 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3826 _editor->zoom_rect->hide();
3830 MouseZoomDrag::aborted (bool)
3832 _editor->zoom_rect->hide ();
3835 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3837 , _cumulative_dx (0)
3838 , _cumulative_dy (0)
3840 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3842 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3843 _region = &_primary->region_view ();
3844 _note_height = _region->midi_stream_view()->note_height ();
3848 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3850 Drag::start_grab (event);
3852 if (!(_was_selected = _primary->selected())) {
3854 /* tertiary-click means extend selection - we'll do that on button release,
3855 so don't add it here, because otherwise we make it hard to figure
3856 out the "extend-to" range.
3859 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3862 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3865 _region->note_selected (_primary, true);
3867 _region->unique_select (_primary);
3873 /** @return Current total drag x change in frames */
3875 NoteDrag::total_dx () const
3878 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3880 /* primary note time */
3881 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3883 /* new time of the primary note relative to the region position */
3884 frameoffset_t st = n + dx;
3886 /* prevent the note being dragged earlier than the region's position */
3891 /* snap and return corresponding delta */
3892 return _region->snap_frame_to_frame (st) - n;
3895 /** @return Current total drag y change in notes */
3897 NoteDrag::total_dy () const
3899 /* this is `backwards' to make increasing note number go in the right direction */
3900 double const dy = _drags->current_pointer_y() - grab_y();
3905 if (abs (dy) >= _note_height) {
3907 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3909 ndy = (int8_t) floor (dy / _note_height / 2.0);
3913 /* more positive value = higher pitch and higher y-axis position on track,
3914 which is the inverse of the X-centric geometric universe
3921 NoteDrag::motion (GdkEvent *, bool)
3923 /* Total change in x and y since the start of the drag */
3924 frameoffset_t const dx = total_dx ();
3925 int8_t const dy = -total_dy ();
3927 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3928 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3929 double const tdy = dy * _note_height - _cumulative_dy;
3932 _cumulative_dx += tdx;
3933 _cumulative_dy += tdy;
3935 int8_t note_delta = total_dy();
3937 _region->move_selection (tdx, tdy, note_delta);
3940 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3941 (int) floor (_primary->note()->note() + note_delta));
3943 show_verbose_cursor_text (buf);
3948 NoteDrag::finished (GdkEvent* ev, bool moved)
3951 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3953 if (_was_selected) {
3954 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3956 _region->note_deselected (_primary);
3959 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3960 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3962 if (!extend && !add && _region->selection_size() > 1) {
3963 _region->unique_select (_primary);
3964 } else if (extend) {
3965 _region->note_selected (_primary, true, true);
3967 /* it was added during button press */
3972 _region->note_dropped (_primary, total_dx(), total_dy());
3977 NoteDrag::aborted (bool)
3982 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3983 : Drag (editor, item)
3985 , _nothing_to_drag (false)
3987 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3989 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3992 /* get all lines in the automation view */
3993 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3995 /* find those that overlap the ranges being dragged */
3996 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3997 while (i != lines.end ()) {
3998 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4001 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4003 /* check this range against all the AudioRanges that we are using */
4004 list<AudioRange>::const_iterator k = _ranges.begin ();
4005 while (k != _ranges.end()) {
4006 if (k->coverage (r.first, r.second) != OverlapNone) {
4012 /* add it to our list if it overlaps at all */
4013 if (k != _ranges.end()) {
4018 _lines.push_back (n);
4024 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4028 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4030 Drag::start_grab (event, cursor);
4032 /* Get line states before we start changing things */
4033 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4034 i->state = &i->line->get_state ();
4037 if (_ranges.empty()) {
4039 /* No selected time ranges: drag all points */
4040 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4041 uint32_t const N = i->line->npoints ();
4042 for (uint32_t j = 0; j < N; ++j) {
4043 i->points.push_back (i->line->nth (j));
4049 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4051 framecnt_t const half = (i->start + i->end) / 2;
4053 /* find the line that this audio range starts in */
4054 list<Line>::iterator j = _lines.begin();
4055 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4059 if (j != _lines.end()) {
4060 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4062 /* j is the line that this audio range starts in; fade into it;
4063 64 samples length plucked out of thin air.
4066 framepos_t a = i->start + 64;
4071 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4072 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4074 the_list->add (p, the_list->eval (p));
4075 j->line->add_always_in_view (p);
4076 the_list->add (q, the_list->eval (q));
4077 j->line->add_always_in_view (q);
4080 /* same thing for the end */
4083 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4087 if (j != _lines.end()) {
4088 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4090 /* j is the line that this audio range starts in; fade out of it;
4091 64 samples length plucked out of thin air.
4094 framepos_t b = i->end - 64;
4099 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4100 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4102 the_list->add (p, the_list->eval (p));
4103 j->line->add_always_in_view (p);
4104 the_list->add (q, the_list->eval (q));
4105 j->line->add_always_in_view (q);
4109 _nothing_to_drag = true;
4111 /* Find all the points that should be dragged and put them in the relevant
4112 points lists in the Line structs.
4115 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4117 uint32_t const N = i->line->npoints ();
4118 for (uint32_t j = 0; j < N; ++j) {
4120 /* here's a control point on this line */
4121 ControlPoint* p = i->line->nth (j);
4122 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4124 /* see if it's inside a range */
4125 list<AudioRange>::const_iterator k = _ranges.begin ();
4126 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4130 if (k != _ranges.end()) {
4131 /* dragging this point */
4132 _nothing_to_drag = false;
4133 i->points.push_back (p);
4139 if (_nothing_to_drag) {
4143 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4144 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4149 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4151 if (_nothing_to_drag) {
4155 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4156 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4158 /* we are ignoring x position for this drag, so we can just pass in anything */
4159 i->line->drag_motion (0, f, true, false);
4164 AutomationRangeDrag::finished (GdkEvent* event, bool)
4166 if (_nothing_to_drag) {
4170 motion (event, false);
4171 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4172 i->line->end_drag ();
4173 i->line->clear_always_in_view ();
4176 _editor->session()->commit_reversible_command ();
4180 AutomationRangeDrag::aborted (bool)
4182 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4183 i->line->clear_always_in_view ();
4188 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4191 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4192 layer = v->region()->layer ();
4193 initial_y = v->get_canvas_group()->property_y ();
4194 initial_playlist = v->region()->playlist ();
4195 initial_position = v->region()->position ();
4196 initial_end = v->region()->position () + v->region()->length ();
4199 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4203 , _cumulative_dx (0)
4205 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4209 PatchChangeDrag::motion (GdkEvent* ev, bool)
4211 framepos_t f = adjusted_current_frame (ev);
4212 boost::shared_ptr<Region> r = _region_view->region ();
4213 f = max (f, r->position ());
4214 f = min (f, r->last_frame ());
4216 framecnt_t const dxf = f - grab_frame();
4217 double const dxu = _editor->frame_to_unit (dxf);
4218 _patch_change->move (dxu - _cumulative_dx, 0);
4219 _cumulative_dx = dxu;
4223 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4225 if (!movement_occurred) {
4229 boost::shared_ptr<Region> r (_region_view->region ());
4231 framepos_t f = adjusted_current_frame (ev);
4232 f = max (f, r->position ());
4233 f = min (f, r->last_frame ());
4235 _region_view->move_patch_change (
4237 _region_view->frames_to_beats (f - r->position() - r->start())
4242 PatchChangeDrag::aborted (bool)
4244 _patch_change->move (-_cumulative_dx, 0);
4248 PatchChangeDrag::setup_pointer_frame_offset ()
4250 boost::shared_ptr<Region> region = _region_view->region ();
4251 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();