2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/audioregion.h"
33 #include "ardour/dB.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/operations.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/session.h"
42 #include "audio_region_view.h"
43 #include "midi_region_view.h"
44 #include "ardour_ui.h"
45 #include "gui_thread.h"
46 #include "control_point.h"
48 #include "region_gain_line.h"
49 #include "editor_drag.h"
50 #include "audio_time_axis.h"
51 #include "midi_time_axis.h"
52 #include "canvas-note.h"
53 #include "selection.h"
54 #include "midi_selection.h"
55 #include "automation_time_axis.h"
57 #include "editor_cursors.h"
58 #include "mouse_cursors.h"
59 #include "verbose_cursor.h"
62 using namespace ARDOUR;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67 using namespace ArdourCanvas;
69 using Gtkmm2ext::Keyboard;
71 double ControlPointDrag::_zero_gain_fraction = -1.0;
73 DragManager::DragManager (Editor* e)
76 , _current_pointer_frame (0)
80 DragManager::~DragManager ()
85 /** Call abort for each active drag */
91 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
96 if (!_drags.empty ()) {
97 _editor->set_follow_playhead (_old_follow_playhead, false);
106 DragManager::add (Drag* d)
108 d->set_manager (this);
109 _drags.push_back (d);
113 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
115 d->set_manager (this);
116 _drags.push_back (d);
121 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
123 /* Prevent follow playhead during the drag to be nice to the user */
124 _old_follow_playhead = _editor->follow_playhead ();
125 _editor->set_follow_playhead (false);
127 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
129 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
130 (*i)->start_grab (e, c);
134 /** Call end_grab for each active drag.
135 * @return true if any drag reported movement having occurred.
138 DragManager::end_grab (GdkEvent* e)
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->end_grab (e);
155 _editor->set_follow_playhead (_old_follow_playhead, false);
161 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
165 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
167 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
168 bool const t = (*i)->motion_handler (e, from_autoscroll);
179 DragManager::have_item (ArdourCanvas::Item* i) const
181 list<Drag*>::const_iterator j = _drags.begin ();
182 while (j != _drags.end() && (*j)->item () != i) {
186 return j != _drags.end ();
189 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
192 , _pointer_frame_offset (0)
193 , _move_threshold_passed (false)
194 , _raw_grab_frame (0)
196 , _last_pointer_frame (0)
202 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
208 cursor = _editor->which_grabber_cursor ();
211 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
215 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
218 cursor = _editor->which_grabber_cursor ();
221 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
223 if (Keyboard::is_button2_event (&event->button)) {
224 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
225 _y_constrained = true;
226 _x_constrained = false;
228 _y_constrained = false;
229 _x_constrained = true;
232 _x_constrained = false;
233 _y_constrained = false;
236 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
237 setup_pointer_frame_offset ();
238 _grab_frame = adjusted_frame (_raw_grab_frame, event);
239 _last_pointer_frame = _grab_frame;
240 _last_pointer_x = _grab_x;
241 _last_pointer_y = _grab_y;
243 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
247 if (_editor->session() && _editor->session()->transport_rolling()) {
250 _was_rolling = false;
253 switch (_editor->snap_type()) {
254 case SnapToRegionStart:
255 case SnapToRegionEnd:
256 case SnapToRegionSync:
257 case SnapToRegionBoundary:
258 _editor->build_region_boundary_cache ();
265 /** Call to end a drag `successfully'. Ungrabs item and calls
266 * subclass' finished() method.
268 * @param event GDK event, or 0.
269 * @return true if some movement occurred, otherwise false.
272 Drag::end_grab (GdkEvent* event)
274 _editor->stop_canvas_autoscroll ();
276 _item->ungrab (event ? event->button.time : 0);
278 finished (event, _move_threshold_passed);
280 _editor->verbose_cursor()->hide ();
282 return _move_threshold_passed;
286 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
290 if (f > _pointer_frame_offset) {
291 pos = f - _pointer_frame_offset;
295 _editor->snap_to_with_modifier (pos, event);
302 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
304 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
308 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
310 /* check to see if we have moved in any way that matters since the last motion event */
311 if (_move_threshold_passed &&
312 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
313 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
317 pair<framecnt_t, int> const threshold = move_threshold ();
319 bool const old_move_threshold_passed = _move_threshold_passed;
321 if (!from_autoscroll && !_move_threshold_passed) {
323 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
324 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
326 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
329 if (active (_editor->mouse_mode) && _move_threshold_passed) {
331 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
332 if (!from_autoscroll) {
333 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
336 motion (event, _move_threshold_passed != old_move_threshold_passed);
338 _last_pointer_x = _drags->current_pointer_x ();
339 _last_pointer_y = _drags->current_pointer_y ();
340 _last_pointer_frame = adjusted_current_frame (event);
348 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
356 aborted (_move_threshold_passed);
358 _editor->stop_canvas_autoscroll ();
359 _editor->verbose_cursor()->hide ();
363 Drag::show_verbose_cursor_time (framepos_t frame)
365 _editor->verbose_cursor()->set_time (
367 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
368 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
371 _editor->verbose_cursor()->show ();
375 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
377 _editor->verbose_cursor()->show (xoffset);
379 _editor->verbose_cursor()->set_duration (
381 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
382 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
387 Drag::show_verbose_cursor_text (string const & text)
389 _editor->verbose_cursor()->show ();
391 _editor->verbose_cursor()->set (
393 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
394 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
399 struct EditorOrderTimeAxisViewSorter {
400 bool operator() (TimeAxisView* a, TimeAxisView* b) {
401 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
402 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
404 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
408 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
412 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
414 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
415 as some of the regions we are dragging may be on such tracks.
418 TrackViewList track_views = _editor->track_views;
419 track_views.sort (EditorOrderTimeAxisViewSorter ());
421 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
422 _time_axis_views.push_back (*i);
424 TimeAxisView::Children children_list = (*i)->get_child_list ();
425 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
426 _time_axis_views.push_back (j->get());
430 /* the list of views can be empty at this point if this is a region list-insert drag
433 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
434 _views.push_back (DraggingView (*i, this));
437 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
441 RegionDrag::region_going_away (RegionView* v)
443 list<DraggingView>::iterator i = _views.begin ();
444 while (i != _views.end() && i->view != v) {
448 if (i != _views.end()) {
453 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
454 * or -1 if it is not found.
457 RegionDrag::find_time_axis_view (TimeAxisView* t) const
460 int const N = _time_axis_views.size ();
461 while (i < N && _time_axis_views[i] != t) {
472 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
473 : RegionDrag (e, i, p, v),
482 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
484 Drag::start_grab (event, cursor);
486 show_verbose_cursor_time (_last_frame_position);
488 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
489 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
490 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
494 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
496 /* compute the amount of pointer motion in frames, and where
497 the region would be if we moved it by that much.
499 *pending_region_position = adjusted_current_frame (event);
501 framepos_t sync_frame;
502 framecnt_t sync_offset;
505 sync_offset = _primary->region()->sync_offset (sync_dir);
507 /* we don't handle a sync point that lies before zero.
509 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
511 sync_frame = *pending_region_position + (sync_dir*sync_offset);
513 _editor->snap_to_with_modifier (sync_frame, event);
515 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
518 *pending_region_position = _last_frame_position;
521 if (*pending_region_position > max_framepos - _primary->region()->length()) {
522 *pending_region_position = _last_frame_position;
527 /* in locked edit mode, reverse the usual meaning of _x_constrained */
528 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
530 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
532 /* x movement since last time (in pixels) */
533 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
535 /* total x movement */
536 framecnt_t total_dx = *pending_region_position;
537 if (regions_came_from_canvas()) {
538 total_dx = total_dx - grab_frame ();
541 /* check that no regions have gone off the start of the session */
542 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
543 if ((i->view->region()->position() + total_dx) < 0) {
545 *pending_region_position = _last_frame_position;
550 _last_frame_position = *pending_region_position;
557 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
559 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
560 int const n = i->time_axis_view + delta_track;
561 if (n < 0 || n >= int (_time_axis_views.size())) {
562 /* off the top or bottom track */
566 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
567 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
568 /* not a track, or the wrong type */
572 int const l = i->layer + delta_layer;
573 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
574 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
575 If it has, the layers will be munged later anyway, so it's ok.
581 /* all regions being dragged are ok with this change */
586 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
588 assert (!_views.empty ());
590 /* Find the TimeAxisView that the pointer is now over */
591 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
593 /* Bail early if we're not over a track */
594 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
595 if (!rtv || !rtv->is_track()) {
596 _editor->verbose_cursor()->hide ();
600 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
602 /* Here's the current pointer position in terms of time axis view and layer */
603 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
604 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
606 /* Work out the change in x */
607 framepos_t pending_region_position;
608 double const x_delta = compute_x_delta (event, &pending_region_position);
610 /* Work out the change in y */
611 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
612 int delta_layer = current_pointer_layer - _last_pointer_layer;
614 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
615 /* this y movement is not allowed, so do no y movement this time */
616 delta_time_axis_view = 0;
620 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
621 /* haven't reached next snap point, and we're not switching
622 trackviews nor layers. nothing to do.
627 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
629 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
631 RegionView* rv = i->view;
633 if (rv->region()->locked()) {
639 rv->get_time_axis_view().hide_dependent_views (*rv);
641 /* Reparent to a non scrolling group so that we can keep the
642 region selection above all time axis views.
643 Reparenting means that we will have to move the region view
644 later, as the two parent groups have different coordinates.
647 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
649 rv->fake_set_opaque (true);
651 if (!rv->get_time_axis_view().hidden()) {
652 /* the track that this region view is on is hidden, so hide the region too */
653 rv->get_canvas_group()->hide ();
657 /* If we have moved tracks, we'll fudge the layer delta so that the
658 region gets moved back onto layer 0 on its new track; this avoids
659 confusion when dragging regions from non-zero layers onto different
662 int this_delta_layer = delta_layer;
663 if (delta_time_axis_view != 0) {
664 this_delta_layer = - i->layer;
667 /* The TimeAxisView that this region is now on */
668 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
671 rv->set_height (tv->view()->child_height ());
673 /* Update show/hidden status as the region view may have come from a hidden track,
674 or have moved to one.
677 rv->get_canvas_group()->hide ();
679 rv->get_canvas_group()->show ();
682 /* Update the DraggingView */
683 i->time_axis_view += delta_time_axis_view;
684 i->layer += this_delta_layer;
687 _editor->mouse_brush_insert_region (rv, pending_region_position);
692 /* Get the y coordinate of the top of the track that this region is now on */
693 tv->canvas_display()->i2w (x, y);
694 y += _editor->get_trackview_group_vertical_offset();
696 /* And adjust for the layer that it should be on */
697 StreamView* cv = tv->view ();
698 if (cv->layer_display() == Stacked) {
699 y += (cv->layers() - i->layer - 1) * cv->child_height ();
702 /* Now move the region view */
703 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
706 } /* foreach region */
708 _total_x_delta += x_delta;
711 _editor->cursor_group->raise_to_top();
714 if (x_delta != 0 && !_brushing) {
715 show_verbose_cursor_time (_last_frame_position);
718 _last_pointer_time_axis_view += delta_time_axis_view;
719 _last_pointer_layer += delta_layer;
723 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
725 if (_copy && first_move) {
727 /* duplicate the regionview(s) and region(s) */
729 list<DraggingView> new_regionviews;
731 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
733 RegionView* rv = i->view;
734 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
735 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
737 const boost::shared_ptr<const Region> original = rv->region();
738 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
739 region_copy->set_position (original->position());
743 boost::shared_ptr<AudioRegion> audioregion_copy
744 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
746 nrv = new AudioRegionView (*arv, audioregion_copy);
748 boost::shared_ptr<MidiRegion> midiregion_copy
749 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
750 nrv = new MidiRegionView (*mrv, midiregion_copy);
755 nrv->get_canvas_group()->show ();
756 new_regionviews.push_back (DraggingView (nrv, this));
758 /* swap _primary to the copy */
760 if (rv == _primary) {
764 /* ..and deselect the one we copied */
766 rv->set_selected (false);
769 if (!new_regionviews.empty()) {
771 /* reflect the fact that we are dragging the copies */
773 _views = new_regionviews;
775 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
778 sync the canvas to what we think is its current state
779 without it, the canvas seems to
780 "forget" to update properly after the upcoming reparent()
781 ..only if the mouse is in rapid motion at the time of the grab.
782 something to do with regionview creation taking so long?
784 _editor->update_canvas_now();
788 RegionMotionDrag::motion (event, first_move);
792 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
794 if (!movement_occurred) {
799 /* reverse this here so that we have the correct logic to finalize
803 if (Config->get_edit_mode() == Lock) {
804 _x_constrained = !_x_constrained;
807 assert (!_views.empty ());
809 /* We might have hidden region views so that they weren't visible during the drag
810 (when they have been reparented). Now everything can be shown again, as region
811 views are back in their track parent groups.
813 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
814 i->view->get_canvas_group()->show ();
817 bool const changed_position = (_last_frame_position != _primary->region()->position());
818 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
819 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
821 _editor->update_canvas_now ();
843 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
845 RegionSelection new_views;
846 PlaylistSet modified_playlists;
847 list<RegionView*> views_to_delete;
850 /* all changes were made during motion event handlers */
852 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
856 _editor->commit_reversible_command ();
860 if (_x_constrained) {
861 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
863 _editor->begin_reversible_command (Operations::region_copy);
866 /* insert the regions into their new playlists */
867 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
869 if (i->view->region()->locked()) {
875 if (changed_position && !_x_constrained) {
876 where = i->view->region()->position() - drag_delta;
878 where = i->view->region()->position();
881 RegionView* new_view = insert_region_into_playlist (
882 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
889 new_views.push_back (new_view);
891 /* we don't need the copied RegionView any more */
892 views_to_delete.push_back (i->view);
895 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
896 because when views are deleted they are automagically removed from _views, which messes
899 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
903 /* If we've created new regions either by copying or moving
904 to a new track, we want to replace the old selection with the new ones
907 if (new_views.size() > 0) {
908 _editor->selection->set (new_views);
911 /* write commands for the accumulated diffs for all our modified playlists */
912 add_stateful_diff_commands_for_playlists (modified_playlists);
914 _editor->commit_reversible_command ();
918 RegionMoveDrag::finished_no_copy (
919 bool const changed_position,
920 bool const changed_tracks,
921 framecnt_t const drag_delta
924 RegionSelection new_views;
925 PlaylistSet modified_playlists;
926 PlaylistSet frozen_playlists;
929 /* all changes were made during motion event handlers */
930 _editor->commit_reversible_command ();
934 if (_x_constrained) {
935 _editor->begin_reversible_command (_("fixed time region drag"));
937 _editor->begin_reversible_command (Operations::region_drag);
940 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
942 RegionView* rv = i->view;
944 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
945 layer_t const dest_layer = i->layer;
947 if (rv->region()->locked()) {
954 if (changed_position && !_x_constrained) {
955 where = rv->region()->position() - drag_delta;
957 where = rv->region()->position();
960 if (changed_tracks) {
962 /* insert into new playlist */
964 RegionView* new_view = insert_region_into_playlist (
965 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
973 new_views.push_back (new_view);
975 /* remove from old playlist */
977 /* the region that used to be in the old playlist is not
978 moved to the new one - we use a copy of it. as a result,
979 any existing editor for the region should no longer be
982 rv->hide_region_editor();
983 rv->fake_set_opaque (false);
985 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
989 rv->region()->clear_changes ();
992 motion on the same track. plonk the previously reparented region
993 back to its original canvas group (its streamview).
994 No need to do anything for copies as they are fake regions which will be deleted.
997 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
998 rv->get_canvas_group()->property_y() = i->initial_y;
999 rv->get_time_axis_view().reveal_dependent_views (*rv);
1001 /* just change the model */
1003 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1005 if (dest_rtv->view()->layer_display() == Stacked) {
1006 rv->region()->set_layer (dest_layer);
1007 rv->region()->set_pending_explicit_relayer (true);
1010 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1012 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1015 playlist->freeze ();
1018 /* this movement may result in a crossfade being modified, so we need to get undo
1019 data from the playlist as well as the region.
1022 r = modified_playlists.insert (playlist);
1024 playlist->clear_changes ();
1027 rv->region()->set_position (where);
1029 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1032 if (changed_tracks) {
1034 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1035 was selected in all of them, then removing it from a playlist will have removed all
1036 trace of it from _views (i.e. there were N regions selected, we removed 1,
1037 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1038 corresponding regionview, and _views is now empty).
1040 This could have invalidated any and all iterators into _views.
1042 The heuristic we use here is: if the region selection is empty, break out of the loop
1043 here. if the region selection is not empty, then restart the loop because we know that
1044 we must have removed at least the region(view) we've just been working on as well as any
1045 that we processed on previous iterations.
1047 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1048 we can just iterate.
1052 if (_views.empty()) {
1063 /* If we've created new regions either by copying or moving
1064 to a new track, we want to replace the old selection with the new ones
1067 if (new_views.size() > 0) {
1068 _editor->selection->set (new_views);
1071 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1075 /* write commands for the accumulated diffs for all our modified playlists */
1076 add_stateful_diff_commands_for_playlists (modified_playlists);
1078 _editor->commit_reversible_command ();
1081 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1082 * @param region Region to remove.
1083 * @param playlist playlist To remove from.
1084 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1085 * that clear_changes () is only called once per playlist.
1088 RegionMoveDrag::remove_region_from_playlist (
1089 boost::shared_ptr<Region> region,
1090 boost::shared_ptr<Playlist> playlist,
1091 PlaylistSet& modified_playlists
1094 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1097 playlist->clear_changes ();
1100 playlist->remove_region (region);
1104 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1105 * clearing the playlist's diff history first if necessary.
1106 * @param region Region to insert.
1107 * @param dest_rtv Destination RouteTimeAxisView.
1108 * @param dest_layer Destination layer.
1109 * @param where Destination position.
1110 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1111 * that clear_changes () is only called once per playlist.
1112 * @return New RegionView, or 0 if no insert was performed.
1115 RegionMoveDrag::insert_region_into_playlist (
1116 boost::shared_ptr<Region> region,
1117 RouteTimeAxisView* dest_rtv,
1120 PlaylistSet& modified_playlists
1123 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1124 if (!dest_playlist) {
1128 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1129 _new_region_view = 0;
1130 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1132 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1133 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1135 dest_playlist->clear_changes ();
1138 dest_playlist->add_region (region, where);
1140 if (dest_rtv->view()->layer_display() == Stacked) {
1141 region->set_layer (dest_layer);
1142 region->set_pending_explicit_relayer (true);
1147 assert (_new_region_view);
1149 return _new_region_view;
1153 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1155 _new_region_view = rv;
1159 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1161 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1162 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1164 _editor->session()->add_command (c);
1173 RegionMoveDrag::aborted (bool movement_occurred)
1177 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1184 RegionMotionDrag::aborted (movement_occurred);
1189 RegionMotionDrag::aborted (bool)
1191 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1192 RegionView* rv = i->view;
1193 TimeAxisView* tv = &(rv->get_time_axis_view ());
1194 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1196 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1197 rv->get_canvas_group()->property_y() = 0;
1198 rv->get_time_axis_view().reveal_dependent_views (*rv);
1199 rv->fake_set_opaque (false);
1200 rv->move (-_total_x_delta, 0);
1201 rv->set_height (rtv->view()->child_height ());
1204 _editor->update_canvas_now ();
1207 /** @param b true to brush, otherwise false.
1208 * @param c true to make copies of the regions being moved, otherwise false.
1210 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1211 : RegionMotionDrag (e, i, p, v, b),
1214 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1217 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1218 if (rtv && rtv->is_track()) {
1219 speed = rtv->track()->speed ();
1222 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1226 RegionMoveDrag::setup_pointer_frame_offset ()
1228 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1231 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1232 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1234 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1236 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1237 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1239 _primary = v->view()->create_region_view (r, false, false);
1241 _primary->get_canvas_group()->show ();
1242 _primary->set_position (pos, 0);
1243 _views.push_back (DraggingView (_primary, this));
1245 _last_frame_position = pos;
1247 _item = _primary->get_canvas_group ();
1251 RegionInsertDrag::finished (GdkEvent *, bool)
1253 _editor->update_canvas_now ();
1255 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1257 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1258 _primary->get_canvas_group()->property_y() = 0;
1260 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1262 _editor->begin_reversible_command (Operations::insert_region);
1263 playlist->clear_changes ();
1264 playlist->add_region (_primary->region (), _last_frame_position);
1265 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1266 _editor->commit_reversible_command ();
1274 RegionInsertDrag::aborted (bool)
1281 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1282 : RegionMoveDrag (e, i, p, v, false, false)
1284 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1287 struct RegionSelectionByPosition {
1288 bool operator() (RegionView*a, RegionView* b) {
1289 return a->region()->position () < b->region()->position();
1294 RegionSpliceDrag::motion (GdkEvent* event, bool)
1296 /* Which trackview is this ? */
1298 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1299 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1301 /* The region motion is only processed if the pointer is over
1305 if (!tv || !tv->is_track()) {
1306 /* To make sure we hide the verbose canvas cursor when the mouse is
1307 not held over and audiotrack.
1309 _editor->verbose_cursor()->hide ();
1315 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1321 RegionSelection copy (_editor->selection->regions);
1323 RegionSelectionByPosition cmp;
1326 framepos_t const pf = adjusted_current_frame (event);
1328 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1330 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1336 boost::shared_ptr<Playlist> playlist;
1338 if ((playlist = atv->playlist()) == 0) {
1342 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1347 if (pf < (*i)->region()->last_frame() + 1) {
1351 if (pf > (*i)->region()->first_frame()) {
1357 playlist->shuffle ((*i)->region(), dir);
1362 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1364 RegionMoveDrag::finished (event, movement_occurred);
1368 RegionSpliceDrag::aborted (bool)
1373 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1375 _view (dynamic_cast<MidiTimeAxisView*> (v))
1377 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1383 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1387 _view->playlist()->freeze ();
1390 framepos_t const f = adjusted_current_frame (event);
1391 if (f < grab_frame()) {
1392 _region->set_position (f);
1395 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1396 so that if this region is duplicated, its duplicate starts on
1397 a snap point rather than 1 frame after a snap point. Otherwise things get
1398 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1399 place snapped notes at the start of the region.
1402 framecnt_t const len = abs (f - grab_frame () - 1);
1403 _region->set_length (len < 1 ? 1 : len);
1409 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1411 if (!movement_occurred) {
1414 _view->playlist()->thaw ();
1418 _editor->commit_reversible_command ();
1423 RegionCreateDrag::add_region ()
1425 if (_editor->session()) {
1426 const TempoMap& map (_editor->session()->tempo_map());
1427 framecnt_t pos = grab_frame();
1428 const Meter& m = map.meter_at (pos);
1429 /* not that the frame rate used here can be affected by pull up/down which
1432 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1433 _region = _view->add_region (grab_frame(), len, false);
1438 RegionCreateDrag::aborted (bool)
1441 _view->playlist()->thaw ();
1447 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1451 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1455 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1457 Gdk::Cursor* cursor;
1458 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1459 float x_fraction = cnote->mouse_x_fraction ();
1461 if (x_fraction > 0.0 && x_fraction < 0.25) {
1462 cursor = _editor->cursors()->left_side_trim;
1464 cursor = _editor->cursors()->right_side_trim;
1467 Drag::start_grab (event, cursor);
1469 region = &cnote->region_view();
1471 double const region_start = region->get_position_pixels();
1472 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1474 if (grab_x() <= middle_point) {
1475 cursor = _editor->cursors()->left_side_trim;
1478 cursor = _editor->cursors()->right_side_trim;
1482 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1484 if (event->motion.state & Keyboard::PrimaryModifier) {
1490 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1492 if (ms.size() > 1) {
1493 /* has to be relative, may make no sense otherwise */
1497 /* select this note; if it is already selected, preserve the existing selection,
1498 otherwise make this note the only one selected.
1500 region->note_selected (cnote, cnote->selected ());
1502 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1503 MidiRegionSelection::iterator next;
1506 (*r)->begin_resizing (at_front);
1512 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1514 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1515 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1516 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1521 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1523 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1524 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1525 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1530 NoteResizeDrag::aborted (bool)
1532 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1533 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1534 (*r)->abort_resizing ();
1538 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1541 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1545 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1551 RegionGainDrag::finished (GdkEvent *, bool)
1557 RegionGainDrag::aborted (bool)
1562 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1563 : RegionDrag (e, i, p, v)
1565 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1569 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1572 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1573 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1575 if (tv && tv->is_track()) {
1576 speed = tv->track()->speed();
1579 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1580 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1581 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1583 framepos_t const pf = adjusted_current_frame (event);
1585 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1586 /* Move the contents of the region around without changing the region bounds */
1587 _operation = ContentsTrim;
1588 Drag::start_grab (event, _editor->cursors()->trimmer);
1590 /* These will get overridden for a point trim.*/
1591 if (pf < (region_start + region_length/2)) {
1592 /* closer to front */
1593 _operation = StartTrim;
1594 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1597 _operation = EndTrim;
1598 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1602 switch (_operation) {
1604 show_verbose_cursor_time (region_start);
1605 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1606 i->view->trim_front_starting ();
1610 show_verbose_cursor_time (region_end);
1613 show_verbose_cursor_time (pf);
1617 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1618 i->view->region()->suspend_property_changes ();
1623 TrimDrag::motion (GdkEvent* event, bool first_move)
1625 RegionView* rv = _primary;
1628 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1629 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1630 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1632 if (tv && tv->is_track()) {
1633 speed = tv->track()->speed();
1636 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1642 switch (_operation) {
1644 trim_type = "Region start trim";
1647 trim_type = "Region end trim";
1650 trim_type = "Region content trim";
1654 _editor->begin_reversible_command (trim_type);
1656 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1657 RegionView* rv = i->view;
1658 rv->fake_set_opaque (false);
1659 rv->enable_display (false);
1660 rv->region()->playlist()->clear_owned_changes ();
1662 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1665 arv->temporarily_hide_envelope ();
1668 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1669 insert_result = _editor->motion_frozen_playlists.insert (pl);
1671 if (insert_result.second) {
1677 bool non_overlap_trim = false;
1679 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1680 non_overlap_trim = true;
1683 switch (_operation) {
1685 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1686 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1691 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1692 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1698 bool swap_direction = false;
1700 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1701 swap_direction = true;
1704 framecnt_t frame_delta = 0;
1706 bool left_direction = false;
1707 if (last_pointer_frame() > adjusted_current_frame(event)) {
1708 left_direction = true;
1711 if (left_direction) {
1712 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1714 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1717 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1718 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1724 switch (_operation) {
1726 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1729 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1732 show_verbose_cursor_time (adjusted_current_frame (event));
1739 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1741 if (movement_occurred) {
1742 motion (event, false);
1744 /* This must happen before the region's StatefulDiffCommand is created, as it may
1745 `correct' (ahem) the region's _start from being negative to being zero. It
1746 needs to be zero in the undo record.
1748 if (_operation == StartTrim) {
1749 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1750 i->view->trim_front_ending ();
1754 if (!_editor->selection->selected (_primary)) {
1755 _primary->thaw_after_trim ();
1758 set<boost::shared_ptr<Playlist> > diffed_playlists;
1760 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1761 i->view->thaw_after_trim ();
1762 i->view->enable_display (true);
1763 i->view->fake_set_opaque (true);
1765 /* Trimming one region may affect others on the playlist, so we need
1766 to get undo Commands from the whole playlist rather than just the
1767 region. Use diffed_playlists to make sure we don't diff a given
1768 playlist more than once.
1770 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1771 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1772 vector<Command*> cmds;
1774 _editor->session()->add_commands (cmds);
1775 diffed_playlists.insert (p);
1779 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1783 _editor->motion_frozen_playlists.clear ();
1784 _editor->commit_reversible_command();
1787 /* no mouse movement */
1788 _editor->point_trim (event, adjusted_current_frame (event));
1791 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1792 if (_operation == StartTrim) {
1793 i->view->trim_front_ending ();
1796 i->view->region()->resume_property_changes ();
1801 TrimDrag::aborted (bool movement_occurred)
1803 /* Our motion method is changing model state, so use the Undo system
1804 to cancel. Perhaps not ideal, as this will leave an Undo point
1805 behind which may be slightly odd from the user's point of view.
1810 if (movement_occurred) {
1814 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1815 i->view->region()->resume_property_changes ();
1820 TrimDrag::setup_pointer_frame_offset ()
1822 list<DraggingView>::iterator i = _views.begin ();
1823 while (i != _views.end() && i->view != _primary) {
1827 if (i == _views.end()) {
1831 switch (_operation) {
1833 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1836 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1843 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1847 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1848 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1853 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1855 Drag::start_grab (event, cursor);
1856 show_verbose_cursor_time (adjusted_current_frame(event));
1860 MeterMarkerDrag::setup_pointer_frame_offset ()
1862 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1866 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1870 // create a dummy marker for visual representation of moving the
1871 // section, because whether its a copy or not, we're going to
1872 // leave or lose the original marker (leave if its a copy; lose if its
1873 // not, because we'll remove it from the map).
1875 MeterSection section (_marker->meter());
1877 if (!section.movable()) {
1882 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1884 _marker = new MeterMarker (
1886 *_editor->meter_group,
1887 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1889 *new MeterSection (_marker->meter())
1892 /* use the new marker for the grab */
1893 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1896 TempoMap& map (_editor->session()->tempo_map());
1897 /* remove the section while we drag it */
1898 map.remove_meter (section);
1902 framepos_t const pf = adjusted_current_frame (event);
1903 _marker->set_position (pf);
1904 show_verbose_cursor_time (pf);
1908 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1910 if (!movement_occurred) {
1914 motion (event, false);
1916 Timecode::BBT_Time when;
1918 TempoMap& map (_editor->session()->tempo_map());
1919 map.bbt_time (last_pointer_frame(), when);
1921 if (_copy == true) {
1922 _editor->begin_reversible_command (_("copy meter mark"));
1923 XMLNode &before = map.get_state();
1924 map.add_meter (_marker->meter(), when);
1925 XMLNode &after = map.get_state();
1926 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1927 _editor->commit_reversible_command ();
1930 _editor->begin_reversible_command (_("move meter mark"));
1931 XMLNode &before = map.get_state();
1933 /* we removed it before, so add it back now */
1935 map.add_meter (_marker->meter(), when);
1936 XMLNode &after = map.get_state();
1937 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1938 _editor->commit_reversible_command ();
1941 // delete the dummy marker we used for visual representation while moving.
1942 // a new visual marker will show up automatically.
1947 MeterMarkerDrag::aborted (bool)
1949 _marker->set_position (_marker->meter().frame ());
1951 /* XXX problem: we don't know if we've moved yet, so we don't
1952 know if the marker is a copy yet or not
1955 TempoMap& map (_editor->session()->tempo_map());
1956 /* we removed it before, so add it back now */
1957 map.add_meter (_marker->meter(), _marker->meter().frame());
1958 // delete the dummy marker we used for visual representation while moving.
1959 // a new visual marker will show up automatically.
1963 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1967 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1969 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1974 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1976 Drag::start_grab (event, cursor);
1977 show_verbose_cursor_time (adjusted_current_frame (event));
1981 TempoMarkerDrag::setup_pointer_frame_offset ()
1983 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1987 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
1991 // create a dummy marker for visual representation of moving the
1992 // section, because whether its a copy or not, we're going to
1993 // leave or lose the original marker (leave if its a copy; lose if its
1994 // not, because we'll remove it from the map).
1996 // create a dummy marker for visual representation of moving the copy.
1997 // The actual copying is not done before we reach the finish callback.
2000 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2002 TempoSection section (_marker->tempo());
2004 _marker = new TempoMarker (
2006 *_editor->tempo_group,
2007 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2009 *new TempoSection (_marker->tempo())
2012 /* use the new marker for the grab */
2013 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2016 TempoMap& map (_editor->session()->tempo_map());
2017 /* remove the section while we drag it */
2018 map.remove_tempo (section);
2022 framepos_t const pf = adjusted_current_frame (event);
2023 _marker->set_position (pf);
2024 show_verbose_cursor_time (pf);
2028 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2030 if (!movement_occurred) {
2034 motion (event, false);
2036 Timecode::BBT_Time when;
2038 TempoMap& map (_editor->session()->tempo_map());
2039 map.bbt_time (last_pointer_frame(), when);
2041 if (_copy == true) {
2042 _editor->begin_reversible_command (_("copy tempo mark"));
2043 XMLNode &before = map.get_state();
2044 map.add_tempo (_marker->tempo(), when);
2045 XMLNode &after = map.get_state();
2046 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2047 _editor->commit_reversible_command ();
2050 _editor->begin_reversible_command (_("move tempo mark"));
2051 XMLNode &before = map.get_state();
2052 /* we removed it before, so add it back now */
2053 map.add_tempo (_marker->tempo(), when);
2054 XMLNode &after = map.get_state();
2055 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2056 _editor->commit_reversible_command ();
2059 // delete the dummy marker we used for visual representation while moving.
2060 // a new visual marker will show up automatically.
2065 TempoMarkerDrag::aborted (bool)
2067 _marker->set_position (_marker->tempo().frame());
2068 /* XXX problem: we don't know if we've moved yet, so we don't
2069 know if the marker is a copy yet or not
2071 TempoMap& map (_editor->session()->tempo_map());
2072 /* we removed it before, so add it back now */
2073 map.add_tempo (_marker->tempo(), _marker->tempo().frame());
2074 // delete the dummy marker we used for visual representation while moving.
2075 // a new visual marker will show up automatically.
2079 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2083 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2086 /** Do all the things we do when dragging the playhead to make it look as though
2087 * we have located, without actually doing the locate (because that would cause
2088 * the diskstream buffers to be refilled, which is too slow).
2091 CursorDrag::fake_locate (framepos_t t)
2093 _editor->playhead_cursor->set_position (t);
2095 Session* s = _editor->session ();
2096 if (s->timecode_transmission_suspended ()) {
2097 framepos_t const f = _editor->playhead_cursor->current_frame;
2098 s->send_mmc_locate (f);
2099 s->send_full_time_code (f);
2102 show_verbose_cursor_time (t);
2103 _editor->UpdateAllTransportClocks (t);
2107 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2109 Drag::start_grab (event, c);
2111 _grab_zoom = _editor->frames_per_unit;
2113 framepos_t where = _editor->event_frame (event, 0, 0);
2114 _editor->snap_to_with_modifier (where, event);
2116 _editor->_dragging_playhead = true;
2118 Session* s = _editor->session ();
2121 if (_was_rolling && _stop) {
2125 if (s->is_auditioning()) {
2126 s->cancel_audition ();
2129 s->request_suspend_timecode_transmission ();
2130 while (!s->timecode_transmission_suspended ()) {
2131 /* twiddle our thumbs */
2135 fake_locate (where);
2139 CursorDrag::motion (GdkEvent* event, bool)
2141 framepos_t const adjusted_frame = adjusted_current_frame (event);
2142 if (adjusted_frame != last_pointer_frame()) {
2143 fake_locate (adjusted_frame);
2145 _editor->update_canvas_now ();
2151 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2153 _editor->_dragging_playhead = false;
2155 if (!movement_occurred && _stop) {
2159 motion (event, false);
2161 Session* s = _editor->session ();
2163 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2164 _editor->_pending_locate_request = true;
2165 s->request_resume_timecode_transmission ();
2170 CursorDrag::aborted (bool)
2172 if (_editor->_dragging_playhead) {
2173 _editor->session()->request_resume_timecode_transmission ();
2174 _editor->_dragging_playhead = false;
2177 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2180 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2181 : RegionDrag (e, i, p, v)
2183 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2187 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2189 Drag::start_grab (event, cursor);
2191 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2192 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2194 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2196 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2200 FadeInDrag::setup_pointer_frame_offset ()
2202 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2203 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2204 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2208 FadeInDrag::motion (GdkEvent* event, bool)
2210 framecnt_t fade_length;
2212 framepos_t const pos = adjusted_current_frame (event);
2214 boost::shared_ptr<Region> region = _primary->region ();
2216 if (pos < (region->position() + 64)) {
2217 fade_length = 64; // this should be a minimum defined somewhere
2218 } else if (pos > region->last_frame()) {
2219 fade_length = region->length();
2221 fade_length = pos - region->position();
2224 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2226 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2232 tmp->reset_fade_in_shape_width (fade_length);
2233 tmp->show_fade_line((framecnt_t) fade_length);
2236 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2240 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2242 if (!movement_occurred) {
2246 framecnt_t fade_length;
2248 framepos_t const pos = adjusted_current_frame (event);
2250 boost::shared_ptr<Region> region = _primary->region ();
2252 if (pos < (region->position() + 64)) {
2253 fade_length = 64; // this should be a minimum defined somewhere
2254 } else if (pos > region->last_frame()) {
2255 fade_length = region->length();
2257 fade_length = pos - region->position();
2260 _editor->begin_reversible_command (_("change fade in length"));
2262 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2264 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2270 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2271 XMLNode &before = alist->get_state();
2273 tmp->audio_region()->set_fade_in_length (fade_length);
2274 tmp->audio_region()->set_fade_in_active (true);
2275 tmp->hide_fade_line();
2277 XMLNode &after = alist->get_state();
2278 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2281 _editor->commit_reversible_command ();
2285 FadeInDrag::aborted (bool)
2287 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2288 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2294 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2295 tmp->hide_fade_line();
2299 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2300 : RegionDrag (e, i, p, v)
2302 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2306 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2308 Drag::start_grab (event, cursor);
2310 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2311 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2313 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2315 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2319 FadeOutDrag::setup_pointer_frame_offset ()
2321 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2322 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2323 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2327 FadeOutDrag::motion (GdkEvent* event, bool)
2329 framecnt_t fade_length;
2331 framepos_t const pos = adjusted_current_frame (event);
2333 boost::shared_ptr<Region> region = _primary->region ();
2335 if (pos > (region->last_frame() - 64)) {
2336 fade_length = 64; // this should really be a minimum fade defined somewhere
2338 else if (pos < region->position()) {
2339 fade_length = region->length();
2342 fade_length = region->last_frame() - pos;
2345 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2347 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2353 tmp->reset_fade_out_shape_width (fade_length);
2354 tmp->show_fade_line(region->length() - fade_length);
2357 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2361 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2363 if (!movement_occurred) {
2367 framecnt_t fade_length;
2369 framepos_t const pos = adjusted_current_frame (event);
2371 boost::shared_ptr<Region> region = _primary->region ();
2373 if (pos > (region->last_frame() - 64)) {
2374 fade_length = 64; // this should really be a minimum fade defined somewhere
2376 else if (pos < region->position()) {
2377 fade_length = region->length();
2380 fade_length = region->last_frame() - pos;
2383 _editor->begin_reversible_command (_("change fade out length"));
2385 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2387 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2393 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2394 XMLNode &before = alist->get_state();
2396 tmp->audio_region()->set_fade_out_length (fade_length);
2397 tmp->audio_region()->set_fade_out_active (true);
2398 tmp->hide_fade_line();
2400 XMLNode &after = alist->get_state();
2401 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2404 _editor->commit_reversible_command ();
2408 FadeOutDrag::aborted (bool)
2410 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2411 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2417 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2418 tmp->hide_fade_line();
2422 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2425 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2427 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2430 _points.push_back (Gnome::Art::Point (0, 0));
2431 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2434 MarkerDrag::~MarkerDrag ()
2436 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2442 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2444 Drag::start_grab (event, cursor);
2448 Location *location = _editor->find_location_from_marker (_marker, is_start);
2449 _editor->_dragging_edit_point = true;
2451 update_item (location);
2453 // _drag_line->show();
2454 // _line->raise_to_top();
2457 show_verbose_cursor_time (location->start());
2459 show_verbose_cursor_time (location->end());
2462 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2465 case Selection::Toggle:
2466 _editor->selection->toggle (_marker);
2468 case Selection::Set:
2469 if (!_editor->selection->selected (_marker)) {
2470 _editor->selection->set (_marker);
2473 case Selection::Extend:
2475 Locations::LocationList ll;
2476 list<Marker*> to_add;
2478 _editor->selection->markers.range (s, e);
2479 s = min (_marker->position(), s);
2480 e = max (_marker->position(), e);
2483 if (e < max_framepos) {
2486 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2487 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2488 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2491 to_add.push_back (lm->start);
2494 to_add.push_back (lm->end);
2498 if (!to_add.empty()) {
2499 _editor->selection->add (to_add);
2503 case Selection::Add:
2504 _editor->selection->add (_marker);
2508 /* Set up copies for us to manipulate during the drag */
2510 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2511 Location* l = _editor->find_location_from_marker (*i, is_start);
2512 _copied_locations.push_back (new Location (*l));
2517 MarkerDrag::setup_pointer_frame_offset ()
2520 Location *location = _editor->find_location_from_marker (_marker, is_start);
2521 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2525 MarkerDrag::motion (GdkEvent* event, bool)
2527 framecnt_t f_delta = 0;
2529 bool move_both = false;
2531 Location *real_location;
2532 Location *copy_location = 0;
2534 framepos_t const newframe = adjusted_current_frame (event);
2536 framepos_t next = newframe;
2538 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2542 MarkerSelection::iterator i;
2543 list<Location*>::iterator x;
2545 /* find the marker we're dragging, and compute the delta */
2547 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2548 x != _copied_locations.end() && i != _editor->selection->markers.end();
2554 if (marker == _marker) {
2556 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2561 if (real_location->is_mark()) {
2562 f_delta = newframe - copy_location->start();
2566 switch (marker->type()) {
2567 case Marker::SessionStart:
2568 case Marker::RangeStart:
2569 case Marker::LoopStart:
2570 case Marker::PunchIn:
2571 f_delta = newframe - copy_location->start();
2574 case Marker::SessionEnd:
2575 case Marker::RangeEnd:
2576 case Marker::LoopEnd:
2577 case Marker::PunchOut:
2578 f_delta = newframe - copy_location->end();
2581 /* what kind of marker is this ? */
2589 if (i == _editor->selection->markers.end()) {
2590 /* hmm, impossible - we didn't find the dragged marker */
2594 /* now move them all */
2596 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2597 x != _copied_locations.end() && i != _editor->selection->markers.end();
2603 /* call this to find out if its the start or end */
2605 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2609 if (real_location->locked()) {
2613 if (copy_location->is_mark()) {
2617 copy_location->set_start (copy_location->start() + f_delta);
2621 framepos_t new_start = copy_location->start() + f_delta;
2622 framepos_t new_end = copy_location->end() + f_delta;
2624 if (is_start) { // start-of-range marker
2627 copy_location->set_start (new_start);
2628 copy_location->set_end (new_end);
2629 } else if (new_start < copy_location->end()) {
2630 copy_location->set_start (new_start);
2631 } else if (newframe > 0) {
2632 _editor->snap_to (next, 1, true);
2633 copy_location->set_end (next);
2634 copy_location->set_start (newframe);
2637 } else { // end marker
2640 copy_location->set_end (new_end);
2641 copy_location->set_start (new_start);
2642 } else if (new_end > copy_location->start()) {
2643 copy_location->set_end (new_end);
2644 } else if (newframe > 0) {
2645 _editor->snap_to (next, -1, true);
2646 copy_location->set_start (next);
2647 copy_location->set_end (newframe);
2652 update_item (copy_location);
2654 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2657 lm->set_position (copy_location->start(), copy_location->end());
2661 assert (!_copied_locations.empty());
2663 show_verbose_cursor_time (newframe);
2666 _editor->update_canvas_now ();
2671 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2673 if (!movement_occurred) {
2675 /* just a click, do nothing but finish
2676 off the selection process
2679 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2682 case Selection::Set:
2683 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2684 _editor->selection->set (_marker);
2688 case Selection::Toggle:
2689 case Selection::Extend:
2690 case Selection::Add:
2697 _editor->_dragging_edit_point = false;
2699 _editor->begin_reversible_command ( _("move marker") );
2700 XMLNode &before = _editor->session()->locations()->get_state();
2702 MarkerSelection::iterator i;
2703 list<Location*>::iterator x;
2706 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2707 x != _copied_locations.end() && i != _editor->selection->markers.end();
2710 Location * location = _editor->find_location_from_marker (*i, is_start);
2714 if (location->locked()) {
2718 if (location->is_mark()) {
2719 location->set_start ((*x)->start());
2721 location->set ((*x)->start(), (*x)->end());
2726 XMLNode &after = _editor->session()->locations()->get_state();
2727 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2728 _editor->commit_reversible_command ();
2732 MarkerDrag::aborted (bool)
2738 MarkerDrag::update_item (Location*)
2743 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2745 _cumulative_x_drag (0),
2746 _cumulative_y_drag (0)
2748 if (_zero_gain_fraction < 0.0) {
2749 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2752 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2754 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2760 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2762 Drag::start_grab (event, _editor->cursors()->fader);
2764 // start the grab at the center of the control point so
2765 // the point doesn't 'jump' to the mouse after the first drag
2766 _fixed_grab_x = _point->get_x();
2767 _fixed_grab_y = _point->get_y();
2769 float const fraction = 1 - (_point->get_y() / _point->line().height());
2771 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2773 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2774 event->button.x + 10, event->button.y + 10);
2776 _editor->verbose_cursor()->show ();
2780 ControlPointDrag::motion (GdkEvent* event, bool)
2782 double dx = _drags->current_pointer_x() - last_pointer_x();
2783 double dy = _drags->current_pointer_y() - last_pointer_y();
2785 if (event->button.state & Keyboard::SecondaryModifier) {
2790 /* coordinate in pixels relative to the start of the region (for region-based automation)
2791 or track (for track-based automation) */
2792 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2793 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2795 // calculate zero crossing point. back off by .01 to stay on the
2796 // positive side of zero
2797 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2799 // make sure we hit zero when passing through
2800 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2804 if (_x_constrained) {
2807 if (_y_constrained) {
2811 _cumulative_x_drag = cx - _fixed_grab_x;
2812 _cumulative_y_drag = cy - _fixed_grab_y;
2816 cy = min ((double) _point->line().height(), cy);
2818 framepos_t cx_frames = _editor->unit_to_frame (cx);
2820 if (!_x_constrained) {
2821 _editor->snap_to_with_modifier (cx_frames, event);
2824 cx_frames = min (cx_frames, _point->line().maximum_time());
2826 float const fraction = 1.0 - (cy / _point->line().height());
2828 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2830 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2832 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2836 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2838 if (!movement_occurred) {
2842 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2843 _editor->reset_point_selection ();
2847 motion (event, false);
2850 _point->line().end_drag ();
2851 _editor->session()->commit_reversible_command ();
2855 ControlPointDrag::aborted (bool)
2857 _point->line().reset ();
2861 ControlPointDrag::active (Editing::MouseMode m)
2863 if (m == Editing::MouseGain) {
2864 /* always active in mouse gain */
2868 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2869 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2872 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2875 _cumulative_y_drag (0)
2877 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2881 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2883 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2886 _item = &_line->grab_item ();
2888 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2889 origin, and ditto for y.
2892 double cx = event->button.x;
2893 double cy = event->button.y;
2895 _line->parent_group().w2i (cx, cy);
2897 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2902 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2903 /* no adjacent points */
2907 Drag::start_grab (event, _editor->cursors()->fader);
2909 /* store grab start in parent frame */
2914 double fraction = 1.0 - (cy / _line->height());
2916 _line->start_drag_line (before, after, fraction);
2918 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2919 event->button.x + 10, event->button.y + 10);
2921 _editor->verbose_cursor()->show ();
2925 LineDrag::motion (GdkEvent* event, bool)
2927 double dy = _drags->current_pointer_y() - last_pointer_y();
2929 if (event->button.state & Keyboard::SecondaryModifier) {
2933 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2935 _cumulative_y_drag = cy - _fixed_grab_y;
2938 cy = min ((double) _line->height(), cy);
2940 double const fraction = 1.0 - (cy / _line->height());
2944 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2950 /* we are ignoring x position for this drag, so we can just pass in anything */
2951 _line->drag_motion (0, fraction, true, push);
2953 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2957 LineDrag::finished (GdkEvent* event, bool)
2959 motion (event, false);
2961 _editor->session()->commit_reversible_command ();
2965 LineDrag::aborted (bool)
2970 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2973 _cumulative_x_drag (0)
2975 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2979 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2981 Drag::start_grab (event);
2983 _line = reinterpret_cast<Line*> (_item);
2986 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2988 double cx = event->button.x;
2989 double cy = event->button.y;
2991 _item->property_parent().get_value()->w2i(cx, cy);
2993 /* store grab start in parent frame */
2994 _region_view_grab_x = cx;
2996 _before = *(float*) _item->get_data ("position");
2998 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3000 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3004 FeatureLineDrag::motion (GdkEvent*, bool)
3006 double dx = _drags->current_pointer_x() - last_pointer_x();
3008 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3010 _cumulative_x_drag += dx;
3012 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3021 ArdourCanvas::Points points;
3023 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3025 _line->get_bounds(x1, y2, x2, y2);
3027 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3028 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3030 _line->property_points() = points;
3032 float *pos = new float;
3035 _line->set_data ("position", pos);
3041 FeatureLineDrag::finished (GdkEvent*, bool)
3043 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3044 _arv->update_transient(_before, _before);
3048 FeatureLineDrag::aborted (bool)
3053 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3056 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3060 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3062 Drag::start_grab (event);
3063 show_verbose_cursor_time (adjusted_current_frame (event));
3067 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3074 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3076 framepos_t grab = grab_frame ();
3077 if (Config->get_rubberbanding_snaps_to_grid ()) {
3078 _editor->snap_to_with_modifier (grab, event);
3081 /* base start and end on initial click position */
3091 if (_drags->current_pointer_y() < grab_y()) {
3092 y1 = _drags->current_pointer_y();
3095 y2 = _drags->current_pointer_y();
3100 if (start != end || y1 != y2) {
3102 double x1 = _editor->frame_to_pixel (start);
3103 double x2 = _editor->frame_to_pixel (end);
3105 _editor->rubberband_rect->property_x1() = x1;
3106 _editor->rubberband_rect->property_y1() = y1;
3107 _editor->rubberband_rect->property_x2() = x2;
3108 _editor->rubberband_rect->property_y2() = y2;
3110 _editor->rubberband_rect->show();
3111 _editor->rubberband_rect->raise_to_top();
3113 show_verbose_cursor_time (pf);
3115 do_select_things (event, true);
3120 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3125 if (grab_frame() < last_pointer_frame()) {
3127 x2 = last_pointer_frame ();
3130 x1 = last_pointer_frame ();
3136 if (_drags->current_pointer_y() < grab_y()) {
3137 y1 = _drags->current_pointer_y();
3140 y2 = _drags->current_pointer_y();
3144 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3148 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3150 if (movement_occurred) {
3152 motion (event, false);
3153 do_select_things (event, false);
3161 _editor->rubberband_rect->hide();
3165 RubberbandSelectDrag::aborted (bool)
3167 _editor->rubberband_rect->hide ();
3170 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3171 : RegionDrag (e, i, p, v)
3173 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3177 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3179 Drag::start_grab (event, cursor);
3181 show_verbose_cursor_time (adjusted_current_frame (event));
3185 TimeFXDrag::motion (GdkEvent* event, bool)
3187 RegionView* rv = _primary;
3189 framepos_t const pf = adjusted_current_frame (event);
3191 if (pf > rv->region()->position()) {
3192 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3195 show_verbose_cursor_time (pf);
3199 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3201 _primary->get_time_axis_view().hide_timestretch ();
3203 if (!movement_occurred) {
3207 if (last_pointer_frame() < _primary->region()->position()) {
3208 /* backwards drag of the left edge - not usable */
3212 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3214 float percentage = (double) newlen / (double) _primary->region()->length();
3216 #ifndef USE_RUBBERBAND
3217 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3218 if (_primary->region()->data_type() == DataType::AUDIO) {
3219 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3223 // XXX how do timeFX on multiple regions ?
3228 if (_editor->time_stretch (rs, percentage) == -1) {
3229 error << _("An error occurred while executing time stretch operation") << endmsg;
3234 TimeFXDrag::aborted (bool)
3236 _primary->get_time_axis_view().hide_timestretch ();
3239 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3242 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3246 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3248 Drag::start_grab (event);
3252 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3254 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3258 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3260 if (movement_occurred && _editor->session()) {
3261 /* make sure we stop */
3262 _editor->session()->request_transport_speed (0.0);
3267 ScrubDrag::aborted (bool)
3272 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3276 , _original_pointer_time_axis (-1)
3277 , _last_pointer_time_axis (-1)
3279 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3283 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3285 if (_editor->session() == 0) {
3289 Gdk::Cursor* cursor = 0;
3291 switch (_operation) {
3292 case CreateSelection:
3293 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3298 cursor = _editor->cursors()->selector;
3299 Drag::start_grab (event, cursor);
3302 case SelectionStartTrim:
3303 if (_editor->clicked_axisview) {
3304 _editor->clicked_axisview->order_selection_trims (_item, true);
3306 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3309 case SelectionEndTrim:
3310 if (_editor->clicked_axisview) {
3311 _editor->clicked_axisview->order_selection_trims (_item, false);
3313 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3317 Drag::start_grab (event, cursor);
3321 if (_operation == SelectionMove) {
3322 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3324 show_verbose_cursor_time (adjusted_current_frame (event));
3327 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3331 SelectionDrag::setup_pointer_frame_offset ()
3333 switch (_operation) {
3334 case CreateSelection:
3335 _pointer_frame_offset = 0;
3338 case SelectionStartTrim:
3340 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3343 case SelectionEndTrim:
3344 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3350 SelectionDrag::motion (GdkEvent* event, bool first_move)
3352 framepos_t start = 0;
3356 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3357 if (pending_time_axis.first == 0) {
3361 framepos_t const pending_position = adjusted_current_frame (event);
3363 /* only alter selection if things have changed */
3365 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3369 switch (_operation) {
3370 case CreateSelection:
3372 framepos_t grab = grab_frame ();
3375 _editor->snap_to (grab);
3378 if (pending_position < grab_frame()) {
3379 start = pending_position;
3382 end = pending_position;
3386 /* first drag: Either add to the selection
3387 or create a new selection
3393 /* adding to the selection */
3394 _editor->set_selected_track_as_side_effect (Selection::Add);
3395 //_editor->selection->add (_editor->clicked_axisview);
3396 _editor->clicked_selection = _editor->selection->add (start, end);
3401 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3402 //_editor->selection->set (_editor->clicked_axisview);
3403 _editor->set_selected_track_as_side_effect (Selection::Set);
3406 _editor->clicked_selection = _editor->selection->set (start, end);
3410 /* select the track that we're in */
3411 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3412 // _editor->set_selected_track_as_side_effect (Selection::Add);
3413 _editor->selection->add (pending_time_axis.first);
3414 _added_time_axes.push_back (pending_time_axis.first);
3417 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3418 tracks that we selected in the first place.
3421 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3422 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3424 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3425 while (i != _added_time_axes.end()) {
3427 list<TimeAxisView*>::iterator tmp = i;
3430 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3431 _editor->selection->remove (*i);
3432 _added_time_axes.remove (*i);
3441 case SelectionStartTrim:
3443 start = _editor->selection->time[_editor->clicked_selection].start;
3444 end = _editor->selection->time[_editor->clicked_selection].end;
3446 if (pending_position > end) {
3449 start = pending_position;
3453 case SelectionEndTrim:
3455 start = _editor->selection->time[_editor->clicked_selection].start;
3456 end = _editor->selection->time[_editor->clicked_selection].end;
3458 if (pending_position < start) {
3461 end = pending_position;
3468 start = _editor->selection->time[_editor->clicked_selection].start;
3469 end = _editor->selection->time[_editor->clicked_selection].end;
3471 length = end - start;
3473 start = pending_position;
3474 _editor->snap_to (start);
3476 end = start + length;
3481 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3482 _editor->start_canvas_autoscroll (1, 0);
3486 _editor->selection->replace (_editor->clicked_selection, start, end);
3489 if (_operation == SelectionMove) {
3490 show_verbose_cursor_time(start);
3492 show_verbose_cursor_time(pending_position);
3497 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3499 Session* s = _editor->session();
3501 if (movement_occurred) {
3502 motion (event, false);
3503 /* XXX this is not object-oriented programming at all. ick */
3504 if (_editor->selection->time.consolidate()) {
3505 _editor->selection->TimeChanged ();
3508 /* XXX what if its a music time selection? */
3509 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3510 s->request_play_range (&_editor->selection->time, true);
3515 /* just a click, no pointer movement.*/
3517 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3518 _editor->selection->clear_time();
3521 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3522 _editor->selection->set (_editor->clicked_axisview);
3525 if (s && s->get_play_range () && s->transport_rolling()) {
3526 s->request_stop (false, false);
3531 _editor->stop_canvas_autoscroll ();
3535 SelectionDrag::aborted (bool)
3540 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3545 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3547 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3548 physical_screen_height (_editor->get_window()));
3549 _drag_rect->hide ();
3551 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3552 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3556 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3558 if (_editor->session() == 0) {
3562 Gdk::Cursor* cursor = 0;
3564 if (!_editor->temp_location) {
3565 _editor->temp_location = new Location (*_editor->session());
3568 switch (_operation) {
3569 case CreateRangeMarker:
3570 case CreateTransportMarker:
3571 case CreateCDMarker:
3573 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3578 cursor = _editor->cursors()->selector;
3582 Drag::start_grab (event, cursor);
3584 show_verbose_cursor_time (adjusted_current_frame (event));
3588 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3590 framepos_t start = 0;
3592 ArdourCanvas::SimpleRect *crect;
3594 switch (_operation) {
3595 case CreateRangeMarker:
3596 crect = _editor->range_bar_drag_rect;
3598 case CreateTransportMarker:
3599 crect = _editor->transport_bar_drag_rect;
3601 case CreateCDMarker:
3602 crect = _editor->cd_marker_bar_drag_rect;
3605 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3610 framepos_t const pf = adjusted_current_frame (event);
3612 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3613 framepos_t grab = grab_frame ();
3614 _editor->snap_to (grab);
3616 if (pf < grab_frame()) {
3624 /* first drag: Either add to the selection
3625 or create a new selection.
3630 _editor->temp_location->set (start, end);
3634 update_item (_editor->temp_location);
3636 //_drag_rect->raise_to_top();
3641 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3642 _editor->start_canvas_autoscroll (1, 0);
3646 _editor->temp_location->set (start, end);
3648 double x1 = _editor->frame_to_pixel (start);
3649 double x2 = _editor->frame_to_pixel (end);
3650 crect->property_x1() = x1;
3651 crect->property_x2() = x2;
3653 update_item (_editor->temp_location);
3656 show_verbose_cursor_time (pf);
3661 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3663 Location * newloc = 0;
3667 if (movement_occurred) {
3668 motion (event, false);
3671 switch (_operation) {
3672 case CreateRangeMarker:
3673 case CreateCDMarker:
3675 _editor->begin_reversible_command (_("new range marker"));
3676 XMLNode &before = _editor->session()->locations()->get_state();
3677 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3678 if (_operation == CreateCDMarker) {
3679 flags = Location::IsRangeMarker | Location::IsCDMarker;
3680 _editor->cd_marker_bar_drag_rect->hide();
3683 flags = Location::IsRangeMarker;
3684 _editor->range_bar_drag_rect->hide();
3686 newloc = new Location (
3687 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3690 _editor->session()->locations()->add (newloc, true);
3691 XMLNode &after = _editor->session()->locations()->get_state();
3692 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3693 _editor->commit_reversible_command ();
3697 case CreateTransportMarker:
3698 // popup menu to pick loop or punch
3699 _editor->new_transport_marker_context_menu (&event->button, _item);
3703 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3705 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3710 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3712 if (end == max_framepos) {
3713 end = _editor->session()->current_end_frame ();
3716 if (start == max_framepos) {
3717 start = _editor->session()->current_start_frame ();
3720 switch (_editor->mouse_mode) {
3722 /* find the two markers on either side and then make the selection from it */
3723 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3727 /* find the two markers on either side of the click and make the range out of it */
3728 _editor->selection->set (start, end);
3737 _editor->stop_canvas_autoscroll ();
3741 RangeMarkerBarDrag::aborted (bool)
3747 RangeMarkerBarDrag::update_item (Location* location)
3749 double const x1 = _editor->frame_to_pixel (location->start());
3750 double const x2 = _editor->frame_to_pixel (location->end());
3752 _drag_rect->property_x1() = x1;
3753 _drag_rect->property_x2() = x2;
3756 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3760 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3764 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3766 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3767 Drag::start_grab (event, _editor->cursors()->zoom_out);
3770 Drag::start_grab (event, _editor->cursors()->zoom_in);
3774 show_verbose_cursor_time (adjusted_current_frame (event));
3778 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3783 framepos_t const pf = adjusted_current_frame (event);
3785 framepos_t grab = grab_frame ();
3786 _editor->snap_to_with_modifier (grab, event);
3788 /* base start and end on initial click position */
3800 _editor->zoom_rect->show();
3801 _editor->zoom_rect->raise_to_top();
3804 _editor->reposition_zoom_rect(start, end);
3806 show_verbose_cursor_time (pf);
3811 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3813 if (movement_occurred) {
3814 motion (event, false);
3816 if (grab_frame() < last_pointer_frame()) {
3817 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3819 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3822 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3823 _editor->tav_zoom_step (_zoom_out);
3825 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3829 _editor->zoom_rect->hide();
3833 MouseZoomDrag::aborted (bool)
3835 _editor->zoom_rect->hide ();
3838 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3840 , _cumulative_dx (0)
3841 , _cumulative_dy (0)
3843 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3845 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3846 _region = &_primary->region_view ();
3847 _note_height = _region->midi_stream_view()->note_height ();
3851 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3853 Drag::start_grab (event);
3855 if (!(_was_selected = _primary->selected())) {
3857 /* tertiary-click means extend selection - we'll do that on button release,
3858 so don't add it here, because otherwise we make it hard to figure
3859 out the "extend-to" range.
3862 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3865 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3868 _region->note_selected (_primary, true);
3870 _region->unique_select (_primary);
3876 /** @return Current total drag x change in frames */
3878 NoteDrag::total_dx () const
3881 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3883 /* primary note time */
3884 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3886 /* new time of the primary note in session frames */
3887 frameoffset_t st = n + dx;
3889 framepos_t const rp = _region->region()->position ();
3891 /* prevent the note being dragged earlier than the region's position */
3894 /* snap and return corresponding delta */
3895 return _region->snap_frame_to_frame (st - rp) + rp - n;
3898 /** @return Current total drag y change in note number */
3900 NoteDrag::total_dy () const
3902 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3906 NoteDrag::motion (GdkEvent *, bool)
3908 /* Total change in x and y since the start of the drag */
3909 frameoffset_t const dx = total_dx ();
3910 int8_t const dy = total_dy ();
3912 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3913 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3914 double const tdy = -dy * _note_height - _cumulative_dy;
3917 _cumulative_dx += tdx;
3918 _cumulative_dy += tdy;
3920 int8_t note_delta = total_dy();
3922 _region->move_selection (tdx, tdy, note_delta);
3924 /* the new note value may be the same as the old one, but we
3925 * don't know what that means because the selection may have
3926 * involved more than one note and we might be doing something
3927 * odd with them. so show the note value anyway, always.
3931 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3933 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3934 (int) floor (new_note));
3936 show_verbose_cursor_text (buf);
3941 NoteDrag::finished (GdkEvent* ev, bool moved)
3944 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3946 if (_was_selected) {
3947 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3949 _region->note_deselected (_primary);
3952 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3953 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3955 if (!extend && !add && _region->selection_size() > 1) {
3956 _region->unique_select (_primary);
3957 } else if (extend) {
3958 _region->note_selected (_primary, true, true);
3960 /* it was added during button press */
3965 _region->note_dropped (_primary, total_dx(), total_dy());
3970 NoteDrag::aborted (bool)
3975 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3976 : Drag (editor, item)
3978 , _nothing_to_drag (false)
3980 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3982 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3985 /* get all lines in the automation view */
3986 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3988 /* find those that overlap the ranges being dragged */
3989 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3990 while (i != lines.end ()) {
3991 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3994 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3996 /* check this range against all the AudioRanges that we are using */
3997 list<AudioRange>::const_iterator k = _ranges.begin ();
3998 while (k != _ranges.end()) {
3999 if (k->coverage (r.first, r.second) != OverlapNone) {
4005 /* add it to our list if it overlaps at all */
4006 if (k != _ranges.end()) {
4011 _lines.push_back (n);
4017 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4021 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4023 Drag::start_grab (event, cursor);
4025 /* Get line states before we start changing things */
4026 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4027 i->state = &i->line->get_state ();
4030 if (_ranges.empty()) {
4032 /* No selected time ranges: drag all points */
4033 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4034 uint32_t const N = i->line->npoints ();
4035 for (uint32_t j = 0; j < N; ++j) {
4036 i->points.push_back (i->line->nth (j));
4042 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4044 framecnt_t const half = (i->start + i->end) / 2;
4046 /* find the line that this audio range starts in */
4047 list<Line>::iterator j = _lines.begin();
4048 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4052 if (j != _lines.end()) {
4053 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4055 /* j is the line that this audio range starts in; fade into it;
4056 64 samples length plucked out of thin air.
4059 framepos_t a = i->start + 64;
4064 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4065 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4067 the_list->add (p, the_list->eval (p));
4068 j->line->add_always_in_view (p);
4069 the_list->add (q, the_list->eval (q));
4070 j->line->add_always_in_view (q);
4073 /* same thing for the end */
4076 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4080 if (j != _lines.end()) {
4081 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4083 /* j is the line that this audio range starts in; fade out of it;
4084 64 samples length plucked out of thin air.
4087 framepos_t b = i->end - 64;
4092 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4093 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4095 the_list->add (p, the_list->eval (p));
4096 j->line->add_always_in_view (p);
4097 the_list->add (q, the_list->eval (q));
4098 j->line->add_always_in_view (q);
4102 _nothing_to_drag = true;
4104 /* Find all the points that should be dragged and put them in the relevant
4105 points lists in the Line structs.
4108 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4110 uint32_t const N = i->line->npoints ();
4111 for (uint32_t j = 0; j < N; ++j) {
4113 /* here's a control point on this line */
4114 ControlPoint* p = i->line->nth (j);
4115 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4117 /* see if it's inside a range */
4118 list<AudioRange>::const_iterator k = _ranges.begin ();
4119 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4123 if (k != _ranges.end()) {
4124 /* dragging this point */
4125 _nothing_to_drag = false;
4126 i->points.push_back (p);
4132 if (_nothing_to_drag) {
4136 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4137 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4142 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4144 if (_nothing_to_drag) {
4148 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4149 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4151 /* we are ignoring x position for this drag, so we can just pass in anything */
4152 i->line->drag_motion (0, f, true, false);
4157 AutomationRangeDrag::finished (GdkEvent* event, bool)
4159 if (_nothing_to_drag) {
4163 motion (event, false);
4164 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4165 i->line->end_drag ();
4166 i->line->clear_always_in_view ();
4169 _editor->session()->commit_reversible_command ();
4173 AutomationRangeDrag::aborted (bool)
4175 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4176 i->line->clear_always_in_view ();
4181 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4184 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4185 layer = v->region()->layer ();
4186 initial_y = v->get_canvas_group()->property_y ();
4187 initial_playlist = v->region()->playlist ();
4188 initial_position = v->region()->position ();
4189 initial_end = v->region()->position () + v->region()->length ();
4192 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4196 , _cumulative_dx (0)
4198 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4202 PatchChangeDrag::motion (GdkEvent* ev, bool)
4204 framepos_t f = adjusted_current_frame (ev);
4205 boost::shared_ptr<Region> r = _region_view->region ();
4206 f = max (f, r->position ());
4207 f = min (f, r->last_frame ());
4209 framecnt_t const dxf = f - grab_frame();
4210 double const dxu = _editor->frame_to_unit (dxf);
4211 _patch_change->move (dxu - _cumulative_dx, 0);
4212 _cumulative_dx = dxu;
4216 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4218 if (!movement_occurred) {
4222 boost::shared_ptr<Region> r (_region_view->region ());
4224 framepos_t f = adjusted_current_frame (ev);
4225 f = max (f, r->position ());
4226 f = min (f, r->last_frame ());
4228 _region_view->move_patch_change (
4230 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4235 PatchChangeDrag::aborted (bool)
4237 _patch_change->move (-_cumulative_dx, 0);
4241 PatchChangeDrag::setup_pointer_frame_offset ()
4243 boost::shared_ptr<Region> region = _region_view->region ();
4244 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4247 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4248 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4255 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4257 framepos_t const p = _region_view->region()->position ();
4258 double const y = _region_view->midi_view()->y_position ();
4260 x1 = max ((framepos_t) 0, x1 - p);
4261 x2 = max ((framepos_t) 0, x2 - p);
4262 y1 = max (0.0, y1 - y);
4263 y2 = max (0.0, y2 - y);
4265 _region_view->update_drag_selection (
4266 _editor->frame_to_pixel (x1),
4267 _editor->frame_to_pixel (x2),
4270 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4275 MidiRubberbandSelectDrag::deselect_things ()
4280 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4281 : RubberbandSelectDrag (e, i)
4287 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4289 if (drag_in_progress) {
4290 /* We just want to select things at the end of the drag, not during it */
4294 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4296 _editor->begin_reversible_command (_("rubberband selection"));
4297 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4298 _editor->commit_reversible_command ();
4302 EditorRubberbandSelectDrag::deselect_things ()
4304 if (!getenv("ARDOUR_SAE")) {
4305 _editor->selection->clear_tracks();
4307 _editor->selection->clear_regions();
4308 _editor->selection->clear_points ();
4309 _editor->selection->clear_lines ();
4312 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4320 NoteCreateDrag::~NoteCreateDrag ()
4326 NoteCreateDrag::grid_frames (framepos_t t) const
4329 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4334 return _region_view->region_beats_to_region_frames (grid_beats);
4338 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4340 Drag::start_grab (event, cursor);
4342 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4344 framepos_t pf = _drags->current_pointer_frame ();
4345 framecnt_t const g = grid_frames (pf);
4347 /* Hack so that we always snap to the note that we are over, instead of snapping
4348 to the next one if we're more than halfway through the one we're over.
4350 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4354 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4356 MidiStreamView* sv = _region_view->midi_stream_view ();
4357 double const x = _editor->frame_to_pixel (_note[0]);
4358 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4360 _drag_rect->property_x1() = x;
4361 _drag_rect->property_y1() = y;
4362 _drag_rect->property_x2() = x;
4363 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4365 _drag_rect->property_outline_what() = 0xff;
4366 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4367 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4371 NoteCreateDrag::motion (GdkEvent* event, bool)
4373 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4374 double const x = _editor->frame_to_pixel (_note[1]);
4375 if (_note[1] > _note[0]) {
4376 _drag_rect->property_x2() = x;
4378 _drag_rect->property_x1() = x;
4383 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4385 if (!had_movement) {
4389 framepos_t const start = min (_note[0], _note[1]);
4390 framecnt_t length = abs (_note[0] - _note[1]);
4392 framecnt_t const g = grid_frames (start);
4393 if (_editor->snap_mode() == SnapNormal && length < g) {
4397 _region_view->create_note_at (start, _drag_rect->property_y1(), _region_view->region_frames_to_region_beats (length), true, false);
4401 NoteCreateDrag::y_to_region (double y) const
4404 _region_view->get_canvas_group()->w2i (x, y);
4409 NoteCreateDrag::aborted (bool)