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*, double> 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;
492 if (tv.first->view()->layer_display() == Stacked) {
493 tv.first->view()->set_layer_display (Expanded);
498 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
500 /* compute the amount of pointer motion in frames, and where
501 the region would be if we moved it by that much.
503 *pending_region_position = adjusted_current_frame (event);
505 framepos_t sync_frame;
506 framecnt_t sync_offset;
509 sync_offset = _primary->region()->sync_offset (sync_dir);
511 /* we don't handle a sync point that lies before zero.
513 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
515 sync_frame = *pending_region_position + (sync_dir*sync_offset);
517 _editor->snap_to_with_modifier (sync_frame, event);
519 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
522 *pending_region_position = _last_frame_position;
525 if (*pending_region_position > max_framepos - _primary->region()->length()) {
526 *pending_region_position = _last_frame_position;
531 /* in locked edit mode, reverse the usual meaning of _x_constrained */
532 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
534 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
536 /* x movement since last time (in pixels) */
537 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
539 /* total x movement */
540 framecnt_t total_dx = *pending_region_position;
541 if (regions_came_from_canvas()) {
542 total_dx = total_dx - grab_frame ();
545 /* check that no regions have gone off the start of the session */
546 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
547 if ((i->view->region()->position() + total_dx) < 0) {
549 *pending_region_position = _last_frame_position;
554 _last_frame_position = *pending_region_position;
561 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
563 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
564 int const n = i->time_axis_view + delta_track;
565 if (n < 0 || n >= int (_time_axis_views.size())) {
566 /* off the top or bottom track */
570 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
571 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
572 /* not a track, or the wrong type */
576 double const l = i->layer + delta_layer;
578 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
579 mode to allow the user to place a region below another on layer 0.
582 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
583 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
584 If it has, the layers will be munged later anyway, so it's ok.
590 /* all regions being dragged are ok with this change */
595 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
597 assert (!_views.empty ());
599 /* Find the TimeAxisView that the pointer is now over */
600 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
602 /* Bail early if we're not over a track */
603 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
604 if (!rtv || !rtv->is_track()) {
605 _editor->verbose_cursor()->hide ();
609 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
611 /* Here's the current pointer position in terms of time axis view and layer */
612 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
613 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
615 /* Work out the change in x */
616 framepos_t pending_region_position;
617 double const x_delta = compute_x_delta (event, &pending_region_position);
619 /* Work out the change in y */
620 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
621 double delta_layer = current_pointer_layer - _last_pointer_layer;
623 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
624 /* this y movement is not allowed, so do no y movement this time */
625 delta_time_axis_view = 0;
629 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
630 /* haven't reached next snap point, and we're not switching
631 trackviews nor layers. nothing to do.
636 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
638 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
640 RegionView* rv = i->view;
642 if (rv->region()->locked()) {
648 rv->get_time_axis_view().hide_dependent_views (*rv);
650 /* Reparent to a non scrolling group so that we can keep the
651 region selection above all time axis views.
652 Reparenting means that we will have to move the region view
653 later, as the two parent groups have different coordinates.
656 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
658 rv->fake_set_opaque (true);
660 if (!rv->get_time_axis_view().hidden()) {
661 /* the track that this region view is on is hidden, so hide the region too */
662 rv->get_canvas_group()->hide ();
666 /* If we have moved tracks, we'll fudge the layer delta so that the
667 region gets moved back onto layer 0 on its new track; this avoids
668 confusion when dragging regions from non-zero layers onto different
671 double this_delta_layer = delta_layer;
672 if (delta_time_axis_view != 0) {
673 this_delta_layer = - i->layer;
676 /* The TimeAxisView that this region is now on */
677 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
679 /* Ensure it is moved from stacked -> expanded if appropriate */
680 if (tv->view()->layer_display() == Stacked) {
681 tv->view()->set_layer_display (Expanded);
684 /* We're only allowed to go -ve in layer on Expanded views */
685 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
686 this_delta_layer = - i->layer;
690 rv->set_height (tv->view()->child_height ());
692 /* Update show/hidden status as the region view may have come from a hidden track,
693 or have moved to one.
696 rv->get_canvas_group()->hide ();
698 rv->get_canvas_group()->show ();
701 /* Update the DraggingView */
702 i->time_axis_view += delta_time_axis_view;
703 i->layer += this_delta_layer;
706 _editor->mouse_brush_insert_region (rv, pending_region_position);
711 /* Get the y coordinate of the top of the track that this region is now on */
712 tv->canvas_display()->i2w (x, y);
713 y += _editor->get_trackview_group_vertical_offset();
715 /* And adjust for the layer that it should be on */
716 StreamView* cv = tv->view ();
717 switch (cv->layer_display ()) {
721 y += (cv->layers() - i->layer - 1) * cv->child_height ();
724 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
728 /* Now move the region view */
729 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
732 } /* foreach region */
734 _total_x_delta += x_delta;
737 _editor->cursor_group->raise_to_top();
740 if (x_delta != 0 && !_brushing) {
741 show_verbose_cursor_time (_last_frame_position);
744 _last_pointer_time_axis_view += delta_time_axis_view;
745 _last_pointer_layer += delta_layer;
749 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
751 if (_copy && first_move) {
753 /* duplicate the regionview(s) and region(s) */
755 list<DraggingView> new_regionviews;
757 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
759 RegionView* rv = i->view;
760 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
761 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
763 const boost::shared_ptr<const Region> original = rv->region();
764 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
765 region_copy->set_position (original->position());
769 boost::shared_ptr<AudioRegion> audioregion_copy
770 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
772 nrv = new AudioRegionView (*arv, audioregion_copy);
774 boost::shared_ptr<MidiRegion> midiregion_copy
775 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
776 nrv = new MidiRegionView (*mrv, midiregion_copy);
781 nrv->get_canvas_group()->show ();
782 new_regionviews.push_back (DraggingView (nrv, this));
784 /* swap _primary to the copy */
786 if (rv == _primary) {
790 /* ..and deselect the one we copied */
792 rv->set_selected (false);
795 if (!new_regionviews.empty()) {
797 /* reflect the fact that we are dragging the copies */
799 _views = new_regionviews;
801 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
804 sync the canvas to what we think is its current state
805 without it, the canvas seems to
806 "forget" to update properly after the upcoming reparent()
807 ..only if the mouse is in rapid motion at the time of the grab.
808 something to do with regionview creation taking so long?
810 _editor->update_canvas_now();
814 RegionMotionDrag::motion (event, first_move);
818 RegionMotionDrag::finished (GdkEvent *, bool)
820 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
821 if ((*i)->view()->layer_display() == Expanded) {
822 (*i)->view()->set_layer_display (Stacked);
828 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
830 RegionMotionDrag::finished (ev, movement_occurred);
831 if (!movement_occurred) {
836 /* reverse this here so that we have the correct logic to finalize
840 if (Config->get_edit_mode() == Lock) {
841 _x_constrained = !_x_constrained;
844 assert (!_views.empty ());
846 /* We might have hidden region views so that they weren't visible during the drag
847 (when they have been reparented). Now everything can be shown again, as region
848 views are back in their track parent groups.
850 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
851 i->view->get_canvas_group()->show ();
854 bool const changed_position = (_last_frame_position != _primary->region()->position());
855 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
856 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
858 _editor->update_canvas_now ();
880 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
882 RegionSelection new_views;
883 PlaylistSet modified_playlists;
884 list<RegionView*> views_to_delete;
887 /* all changes were made during motion event handlers */
889 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
893 _editor->commit_reversible_command ();
897 if (_x_constrained) {
898 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
900 _editor->begin_reversible_command (Operations::region_copy);
903 /* insert the regions into their new playlists */
904 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
906 if (i->view->region()->locked()) {
912 if (changed_position && !_x_constrained) {
913 where = i->view->region()->position() - drag_delta;
915 where = i->view->region()->position();
918 RegionView* new_view = insert_region_into_playlist (
919 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
926 new_views.push_back (new_view);
928 /* we don't need the copied RegionView any more */
929 views_to_delete.push_back (i->view);
932 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
933 because when views are deleted they are automagically removed from _views, which messes
936 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
940 /* If we've created new regions either by copying or moving
941 to a new track, we want to replace the old selection with the new ones
944 if (new_views.size() > 0) {
945 _editor->selection->set (new_views);
948 /* write commands for the accumulated diffs for all our modified playlists */
949 add_stateful_diff_commands_for_playlists (modified_playlists);
951 _editor->commit_reversible_command ();
955 RegionMoveDrag::finished_no_copy (
956 bool const changed_position,
957 bool const changed_tracks,
958 framecnt_t const drag_delta
961 RegionSelection new_views;
962 PlaylistSet modified_playlists;
963 PlaylistSet frozen_playlists;
965 list<pair<boost::shared_ptr<Region>, double> > pending_explicit_relayers;
966 Playlist::RegionList pending_implicit_relayers;
967 set<RouteTimeAxisView*> views_to_update;
970 /* all changes were made during motion event handlers */
971 _editor->commit_reversible_command ();
975 if (_x_constrained) {
976 _editor->begin_reversible_command (_("fixed time region drag"));
978 _editor->begin_reversible_command (Operations::region_drag);
981 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
983 RegionView* rv = i->view;
985 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
986 double const dest_layer = i->layer;
988 if (rv->region()->locked()) {
993 views_to_update.insert (dest_rtv);
997 if (changed_position && !_x_constrained) {
998 where = rv->region()->position() - drag_delta;
1000 where = rv->region()->position();
1003 if (changed_tracks) {
1005 /* insert into new playlist */
1007 RegionView* new_view = insert_region_into_playlist (
1008 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1011 if (new_view == 0) {
1016 new_views.push_back (new_view);
1018 /* remove from old playlist */
1020 /* the region that used to be in the old playlist is not
1021 moved to the new one - we use a copy of it. as a result,
1022 any existing editor for the region should no longer be
1025 rv->hide_region_editor();
1026 rv->fake_set_opaque (false);
1028 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1032 rv->region()->clear_changes ();
1035 motion on the same track. plonk the previously reparented region
1036 back to its original canvas group (its streamview).
1037 No need to do anything for copies as they are fake regions which will be deleted.
1040 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1041 rv->get_canvas_group()->property_y() = i->initial_y;
1042 rv->get_time_axis_view().reveal_dependent_views (*rv);
1044 /* just change the model */
1046 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1048 bool const explicit_relayer = dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded;
1050 if (explicit_relayer) {
1051 pending_explicit_relayers.push_back (make_pair (rv->region (), dest_layer));
1053 pending_implicit_relayers.push_back (rv->region ());
1056 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1058 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1061 playlist->freeze ();
1062 playlist->suspend_relayer ();
1065 /* this movement may result in a crossfade being modified, so we need to get undo
1066 data from the playlist as well as the region.
1069 r = modified_playlists.insert (playlist);
1071 playlist->clear_changes ();
1074 rv->region()->set_position (where);
1076 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1079 if (changed_tracks) {
1081 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1082 was selected in all of them, then removing it from a playlist will have removed all
1083 trace of it from _views (i.e. there were N regions selected, we removed 1,
1084 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1085 corresponding regionview, and _views is now empty).
1087 This could have invalidated any and all iterators into _views.
1089 The heuristic we use here is: if the region selection is empty, break out of the loop
1090 here. if the region selection is not empty, then restart the loop because we know that
1091 we must have removed at least the region(view) we've just been working on as well as any
1092 that we processed on previous iterations.
1094 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1095 we can just iterate.
1099 if (_views.empty()) {
1110 /* If we've created new regions either by copying or moving
1111 to a new track, we want to replace the old selection with the new ones
1114 if (new_views.size() > 0) {
1115 _editor->selection->set (new_views);
1118 /* We can't use the normal mechanism for relayering, as some regions may require an explicit relayer
1119 rather than an implicit one. So we thaw before resuming relayering, then do the relayers
1123 for (PlaylistSet::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1125 (*p)->resume_relayer ();
1128 for (list<pair<boost::shared_ptr<Region>, double> >::iterator i = pending_explicit_relayers.begin(); i != pending_explicit_relayers.end(); ++i) {
1129 i->first->playlist()->relayer (i->first, i->second);
1132 for (Playlist::RegionList::iterator i = pending_implicit_relayers.begin(); i != pending_implicit_relayers.end(); ++i) {
1133 (*i)->playlist()->relayer (*i);
1136 /* write commands for the accumulated diffs for all our modified playlists */
1137 add_stateful_diff_commands_for_playlists (modified_playlists);
1139 _editor->commit_reversible_command ();
1141 /* We have futzed with the layering of canvas items on our streamviews.
1142 If any region changed layer, this will have resulted in the stream
1143 views being asked to set up their region views, and all will be
1144 well. If not, we might now have badly-ordered region views. Ask
1145 the Streamviews involved to sort themselves out, just in case.
1148 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1149 (*i)->view()->playlist_layered ((*i)->track ());
1153 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1154 * @param region Region to remove.
1155 * @param playlist playlist To remove from.
1156 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1157 * that clear_changes () is only called once per playlist.
1160 RegionMoveDrag::remove_region_from_playlist (
1161 boost::shared_ptr<Region> region,
1162 boost::shared_ptr<Playlist> playlist,
1163 PlaylistSet& modified_playlists
1166 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1169 playlist->clear_changes ();
1172 playlist->remove_region (region);
1176 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1177 * clearing the playlist's diff history first if necessary.
1178 * @param region Region to insert.
1179 * @param dest_rtv Destination RouteTimeAxisView.
1180 * @param dest_layer Destination layer.
1181 * @param where Destination position.
1182 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1183 * that clear_changes () is only called once per playlist.
1184 * @return New RegionView, or 0 if no insert was performed.
1187 RegionMoveDrag::insert_region_into_playlist (
1188 boost::shared_ptr<Region> region,
1189 RouteTimeAxisView* dest_rtv,
1192 PlaylistSet& modified_playlists
1195 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1196 if (!dest_playlist) {
1200 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1201 _new_region_view = 0;
1202 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1204 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1205 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1207 dest_playlist->clear_changes ();
1210 dest_playlist->add_region (region, where);
1212 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1213 dest_playlist->relayer (region, dest_layer);
1218 assert (_new_region_view);
1220 return _new_region_view;
1224 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1226 _new_region_view = rv;
1230 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1232 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1233 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1235 _editor->session()->add_command (c);
1244 RegionMoveDrag::aborted (bool movement_occurred)
1248 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1255 RegionMotionDrag::aborted (movement_occurred);
1260 RegionMotionDrag::aborted (bool)
1262 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1263 if ((*i)->view()->layer_display() == Expanded) {
1264 (*i)->view()->set_layer_display (Stacked);
1268 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1269 RegionView* rv = i->view;
1270 TimeAxisView* tv = &(rv->get_time_axis_view ());
1271 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1273 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1274 rv->get_canvas_group()->property_y() = 0;
1275 rv->get_time_axis_view().reveal_dependent_views (*rv);
1276 rv->fake_set_opaque (false);
1277 rv->move (-_total_x_delta, 0);
1278 rv->set_height (rtv->view()->child_height ());
1281 _editor->update_canvas_now ();
1284 /** @param b true to brush, otherwise false.
1285 * @param c true to make copies of the regions being moved, otherwise false.
1287 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1288 : RegionMotionDrag (e, i, p, v, b),
1291 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1294 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1295 if (rtv && rtv->is_track()) {
1296 speed = rtv->track()->speed ();
1299 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1303 RegionMoveDrag::setup_pointer_frame_offset ()
1305 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1308 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1309 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1311 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1313 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1314 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1316 _primary = v->view()->create_region_view (r, false, false);
1318 _primary->get_canvas_group()->show ();
1319 _primary->set_position (pos, 0);
1320 _views.push_back (DraggingView (_primary, this));
1322 _last_frame_position = pos;
1324 _item = _primary->get_canvas_group ();
1328 RegionInsertDrag::finished (GdkEvent *, bool)
1330 _editor->update_canvas_now ();
1332 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1334 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1335 _primary->get_canvas_group()->property_y() = 0;
1337 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1339 _editor->begin_reversible_command (Operations::insert_region);
1340 playlist->clear_changes ();
1341 playlist->add_region (_primary->region (), _last_frame_position);
1342 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1343 _editor->commit_reversible_command ();
1351 RegionInsertDrag::aborted (bool)
1358 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1359 : RegionMoveDrag (e, i, p, v, false, false)
1361 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1364 struct RegionSelectionByPosition {
1365 bool operator() (RegionView*a, RegionView* b) {
1366 return a->region()->position () < b->region()->position();
1371 RegionSpliceDrag::motion (GdkEvent* event, bool)
1373 /* Which trackview is this ? */
1375 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1376 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1378 /* The region motion is only processed if the pointer is over
1382 if (!tv || !tv->is_track()) {
1383 /* To make sure we hide the verbose canvas cursor when the mouse is
1384 not held over and audiotrack.
1386 _editor->verbose_cursor()->hide ();
1392 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1398 RegionSelection copy (_editor->selection->regions);
1400 RegionSelectionByPosition cmp;
1403 framepos_t const pf = adjusted_current_frame (event);
1405 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1407 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1413 boost::shared_ptr<Playlist> playlist;
1415 if ((playlist = atv->playlist()) == 0) {
1419 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1424 if (pf < (*i)->region()->last_frame() + 1) {
1428 if (pf > (*i)->region()->first_frame()) {
1434 playlist->shuffle ((*i)->region(), dir);
1439 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1441 RegionMoveDrag::finished (event, movement_occurred);
1445 RegionSpliceDrag::aborted (bool)
1450 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1452 _view (dynamic_cast<MidiTimeAxisView*> (v))
1454 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1460 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1464 _view->playlist()->freeze ();
1467 framepos_t const f = adjusted_current_frame (event);
1468 if (f < grab_frame()) {
1469 _region->set_position (f);
1472 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1473 so that if this region is duplicated, its duplicate starts on
1474 a snap point rather than 1 frame after a snap point. Otherwise things get
1475 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1476 place snapped notes at the start of the region.
1479 framecnt_t const len = abs (f - grab_frame () - 1);
1480 _region->set_length (len < 1 ? 1 : len);
1486 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1488 if (!movement_occurred) {
1491 _view->playlist()->thaw ();
1495 _editor->commit_reversible_command ();
1500 RegionCreateDrag::add_region ()
1502 if (_editor->session()) {
1503 const TempoMap& map (_editor->session()->tempo_map());
1504 framecnt_t pos = grab_frame();
1505 const Meter& m = map.meter_at (pos);
1506 /* not that the frame rate used here can be affected by pull up/down which
1509 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1510 _region = _view->add_region (grab_frame(), len, false);
1515 RegionCreateDrag::aborted (bool)
1518 _view->playlist()->thaw ();
1524 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1528 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1532 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1534 Gdk::Cursor* cursor;
1535 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1536 float x_fraction = cnote->mouse_x_fraction ();
1538 if (x_fraction > 0.0 && x_fraction < 0.25) {
1539 cursor = _editor->cursors()->left_side_trim;
1541 cursor = _editor->cursors()->right_side_trim;
1544 Drag::start_grab (event, cursor);
1546 region = &cnote->region_view();
1548 double const region_start = region->get_position_pixels();
1549 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1551 if (grab_x() <= middle_point) {
1552 cursor = _editor->cursors()->left_side_trim;
1555 cursor = _editor->cursors()->right_side_trim;
1559 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1561 if (event->motion.state & Keyboard::PrimaryModifier) {
1567 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1569 if (ms.size() > 1) {
1570 /* has to be relative, may make no sense otherwise */
1574 /* select this note; if it is already selected, preserve the existing selection,
1575 otherwise make this note the only one selected.
1577 region->note_selected (cnote, cnote->selected ());
1579 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1580 MidiRegionSelection::iterator next;
1583 (*r)->begin_resizing (at_front);
1589 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1591 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1592 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1593 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1598 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1600 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1601 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1602 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1607 NoteResizeDrag::aborted (bool)
1609 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1610 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1611 (*r)->abort_resizing ();
1615 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1618 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1622 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1628 RegionGainDrag::finished (GdkEvent *, bool)
1634 RegionGainDrag::aborted (bool)
1639 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1640 : RegionDrag (e, i, p, v)
1642 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1646 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1649 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1650 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1652 if (tv && tv->is_track()) {
1653 speed = tv->track()->speed();
1656 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1657 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1658 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1660 framepos_t const pf = adjusted_current_frame (event);
1662 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1663 /* Move the contents of the region around without changing the region bounds */
1664 _operation = ContentsTrim;
1665 Drag::start_grab (event, _editor->cursors()->trimmer);
1667 /* These will get overridden for a point trim.*/
1668 if (pf < (region_start + region_length/2)) {
1669 /* closer to front */
1670 _operation = StartTrim;
1671 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1674 _operation = EndTrim;
1675 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1679 switch (_operation) {
1681 show_verbose_cursor_time (region_start);
1682 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1683 i->view->trim_front_starting ();
1687 show_verbose_cursor_time (region_end);
1690 show_verbose_cursor_time (pf);
1694 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1695 i->view->region()->suspend_property_changes ();
1700 TrimDrag::motion (GdkEvent* event, bool first_move)
1702 RegionView* rv = _primary;
1705 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1706 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1707 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1709 if (tv && tv->is_track()) {
1710 speed = tv->track()->speed();
1713 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1719 switch (_operation) {
1721 trim_type = "Region start trim";
1724 trim_type = "Region end trim";
1727 trim_type = "Region content trim";
1731 _editor->begin_reversible_command (trim_type);
1733 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1734 RegionView* rv = i->view;
1735 rv->fake_set_opaque (false);
1736 rv->enable_display (false);
1737 rv->region()->playlist()->clear_owned_changes ();
1739 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1742 arv->temporarily_hide_envelope ();
1745 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1746 insert_result = _editor->motion_frozen_playlists.insert (pl);
1748 if (insert_result.second) {
1754 bool non_overlap_trim = false;
1756 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1757 non_overlap_trim = true;
1760 switch (_operation) {
1762 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1763 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1768 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1769 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1775 bool swap_direction = false;
1777 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1778 swap_direction = true;
1781 framecnt_t frame_delta = 0;
1783 bool left_direction = false;
1784 if (last_pointer_frame() > adjusted_current_frame(event)) {
1785 left_direction = true;
1788 if (left_direction) {
1789 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1791 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1794 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1795 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1801 switch (_operation) {
1803 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1806 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1809 show_verbose_cursor_time (adjusted_current_frame (event));
1816 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1818 if (movement_occurred) {
1819 motion (event, false);
1821 /* This must happen before the region's StatefulDiffCommand is created, as it may
1822 `correct' (ahem) the region's _start from being negative to being zero. It
1823 needs to be zero in the undo record.
1825 if (_operation == StartTrim) {
1826 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1827 i->view->trim_front_ending ();
1831 if (!_editor->selection->selected (_primary)) {
1832 _primary->thaw_after_trim ();
1835 set<boost::shared_ptr<Playlist> > diffed_playlists;
1837 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1838 i->view->thaw_after_trim ();
1839 i->view->enable_display (true);
1840 i->view->fake_set_opaque (true);
1842 /* Trimming one region may affect others on the playlist, so we need
1843 to get undo Commands from the whole playlist rather than just the
1844 region. Use diffed_playlists to make sure we don't diff a given
1845 playlist more than once.
1847 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1848 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1849 vector<Command*> cmds;
1851 _editor->session()->add_commands (cmds);
1852 diffed_playlists.insert (p);
1856 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1860 _editor->motion_frozen_playlists.clear ();
1861 _editor->commit_reversible_command();
1864 /* no mouse movement */
1865 _editor->point_trim (event, adjusted_current_frame (event));
1868 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1869 if (_operation == StartTrim) {
1870 i->view->trim_front_ending ();
1873 i->view->region()->resume_property_changes ();
1878 TrimDrag::aborted (bool movement_occurred)
1880 /* Our motion method is changing model state, so use the Undo system
1881 to cancel. Perhaps not ideal, as this will leave an Undo point
1882 behind which may be slightly odd from the user's point of view.
1887 if (movement_occurred) {
1891 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1892 i->view->region()->resume_property_changes ();
1897 TrimDrag::setup_pointer_frame_offset ()
1899 list<DraggingView>::iterator i = _views.begin ();
1900 while (i != _views.end() && i->view != _primary) {
1904 if (i == _views.end()) {
1908 switch (_operation) {
1910 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1913 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1920 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1924 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1925 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1930 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1932 Drag::start_grab (event, cursor);
1933 show_verbose_cursor_time (adjusted_current_frame(event));
1937 MeterMarkerDrag::setup_pointer_frame_offset ()
1939 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1943 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1947 // create a dummy marker for visual representation of moving the
1948 // section, because whether its a copy or not, we're going to
1949 // leave or lose the original marker (leave if its a copy; lose if its
1950 // not, because we'll remove it from the map).
1952 MeterSection section (_marker->meter());
1954 if (!section.movable()) {
1959 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1961 _marker = new MeterMarker (
1963 *_editor->meter_group,
1964 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1966 *new MeterSection (_marker->meter())
1969 /* use the new marker for the grab */
1970 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1973 TempoMap& map (_editor->session()->tempo_map());
1974 /* remove the section while we drag it */
1975 map.remove_meter (section);
1979 framepos_t const pf = adjusted_current_frame (event);
1980 _marker->set_position (pf);
1981 show_verbose_cursor_time (pf);
1985 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1987 if (!movement_occurred) {
1991 motion (event, false);
1993 Timecode::BBT_Time when;
1995 TempoMap& map (_editor->session()->tempo_map());
1996 map.bbt_time (last_pointer_frame(), when);
1998 if (_copy == true) {
1999 _editor->begin_reversible_command (_("copy meter mark"));
2000 XMLNode &before = map.get_state();
2001 map.add_meter (_marker->meter(), when);
2002 XMLNode &after = map.get_state();
2003 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2004 _editor->commit_reversible_command ();
2007 _editor->begin_reversible_command (_("move meter mark"));
2008 XMLNode &before = map.get_state();
2010 /* we removed it before, so add it back now */
2012 map.add_meter (_marker->meter(), when);
2013 XMLNode &after = map.get_state();
2014 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2015 _editor->commit_reversible_command ();
2018 // delete the dummy marker we used for visual representation while moving.
2019 // a new visual marker will show up automatically.
2024 MeterMarkerDrag::aborted (bool moved)
2026 _marker->set_position (_marker->meter().frame ());
2029 TempoMap& map (_editor->session()->tempo_map());
2030 /* we removed it before, so add it back now */
2031 map.add_meter (_marker->meter(), _marker->meter().frame());
2032 // delete the dummy marker we used for visual representation while moving.
2033 // a new visual marker will show up automatically.
2038 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2042 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2044 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2049 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2051 Drag::start_grab (event, cursor);
2052 show_verbose_cursor_time (adjusted_current_frame (event));
2056 TempoMarkerDrag::setup_pointer_frame_offset ()
2058 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2062 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2066 // create a dummy marker for visual representation of moving the
2067 // section, because whether its a copy or not, we're going to
2068 // leave or lose the original marker (leave if its a copy; lose if its
2069 // not, because we'll remove it from the map).
2071 // create a dummy marker for visual representation of moving the copy.
2072 // The actual copying is not done before we reach the finish callback.
2075 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2077 TempoSection section (_marker->tempo());
2079 _marker = new TempoMarker (
2081 *_editor->tempo_group,
2082 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2084 *new TempoSection (_marker->tempo())
2087 /* use the new marker for the grab */
2088 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2091 TempoMap& map (_editor->session()->tempo_map());
2092 /* remove the section while we drag it */
2093 map.remove_tempo (section);
2097 framepos_t const pf = adjusted_current_frame (event);
2098 _marker->set_position (pf);
2099 show_verbose_cursor_time (pf);
2103 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2105 if (!movement_occurred) {
2109 motion (event, false);
2111 Timecode::BBT_Time when;
2113 TempoMap& map (_editor->session()->tempo_map());
2114 map.bbt_time (last_pointer_frame(), when);
2116 if (_copy == true) {
2117 _editor->begin_reversible_command (_("copy tempo mark"));
2118 XMLNode &before = map.get_state();
2119 map.add_tempo (_marker->tempo(), when);
2120 XMLNode &after = map.get_state();
2121 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2122 _editor->commit_reversible_command ();
2125 _editor->begin_reversible_command (_("move tempo mark"));
2126 XMLNode &before = map.get_state();
2127 /* we removed it before, so add it back now */
2128 map.add_tempo (_marker->tempo(), when);
2129 XMLNode &after = map.get_state();
2130 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2131 _editor->commit_reversible_command ();
2134 // delete the dummy marker we used for visual representation while moving.
2135 // a new visual marker will show up automatically.
2140 TempoMarkerDrag::aborted (bool moved)
2142 _marker->set_position (_marker->tempo().frame());
2144 TempoMap& map (_editor->session()->tempo_map());
2145 /* we removed it before, so add it back now */
2146 map.add_tempo (_marker->tempo(), _marker->tempo().frame());
2147 // delete the dummy marker we used for visual representation while moving.
2148 // a new visual marker will show up automatically.
2153 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2157 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2160 /** Do all the things we do when dragging the playhead to make it look as though
2161 * we have located, without actually doing the locate (because that would cause
2162 * the diskstream buffers to be refilled, which is too slow).
2165 CursorDrag::fake_locate (framepos_t t)
2167 _editor->playhead_cursor->set_position (t);
2169 Session* s = _editor->session ();
2170 if (s->timecode_transmission_suspended ()) {
2171 framepos_t const f = _editor->playhead_cursor->current_frame;
2172 s->send_mmc_locate (f);
2173 s->send_full_time_code (f);
2176 show_verbose_cursor_time (t);
2177 _editor->UpdateAllTransportClocks (t);
2181 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2183 Drag::start_grab (event, c);
2185 _grab_zoom = _editor->frames_per_unit;
2187 framepos_t where = _editor->event_frame (event, 0, 0);
2188 _editor->snap_to_with_modifier (where, event);
2190 _editor->_dragging_playhead = true;
2192 Session* s = _editor->session ();
2195 if (_was_rolling && _stop) {
2199 if (s->is_auditioning()) {
2200 s->cancel_audition ();
2203 s->request_suspend_timecode_transmission ();
2204 while (!s->timecode_transmission_suspended ()) {
2205 /* twiddle our thumbs */
2209 fake_locate (where);
2213 CursorDrag::motion (GdkEvent* event, bool)
2215 framepos_t const adjusted_frame = adjusted_current_frame (event);
2216 if (adjusted_frame != last_pointer_frame()) {
2217 fake_locate (adjusted_frame);
2219 _editor->update_canvas_now ();
2225 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2227 _editor->_dragging_playhead = false;
2229 if (!movement_occurred && _stop) {
2233 motion (event, false);
2235 Session* s = _editor->session ();
2237 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2238 _editor->_pending_locate_request = true;
2239 s->request_resume_timecode_transmission ();
2244 CursorDrag::aborted (bool)
2246 if (_editor->_dragging_playhead) {
2247 _editor->session()->request_resume_timecode_transmission ();
2248 _editor->_dragging_playhead = false;
2251 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2254 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2255 : RegionDrag (e, i, p, v)
2257 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2261 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2263 Drag::start_grab (event, cursor);
2265 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2266 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2268 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2270 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2274 FadeInDrag::setup_pointer_frame_offset ()
2276 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2277 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2278 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2282 FadeInDrag::motion (GdkEvent* event, bool)
2284 framecnt_t fade_length;
2286 framepos_t const pos = adjusted_current_frame (event);
2288 boost::shared_ptr<Region> region = _primary->region ();
2290 if (pos < (region->position() + 64)) {
2291 fade_length = 64; // this should be a minimum defined somewhere
2292 } else if (pos > region->last_frame()) {
2293 fade_length = region->length();
2295 fade_length = pos - region->position();
2298 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2300 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2306 tmp->reset_fade_in_shape_width (fade_length);
2307 tmp->show_fade_line((framecnt_t) fade_length);
2310 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2314 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2316 if (!movement_occurred) {
2320 framecnt_t fade_length;
2322 framepos_t const pos = adjusted_current_frame (event);
2324 boost::shared_ptr<Region> region = _primary->region ();
2326 if (pos < (region->position() + 64)) {
2327 fade_length = 64; // this should be a minimum defined somewhere
2328 } else if (pos > region->last_frame()) {
2329 fade_length = region->length();
2331 fade_length = pos - region->position();
2334 _editor->begin_reversible_command (_("change fade in length"));
2336 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2338 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2344 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2345 XMLNode &before = alist->get_state();
2347 tmp->audio_region()->set_fade_in_length (fade_length);
2348 tmp->audio_region()->set_fade_in_active (true);
2349 tmp->hide_fade_line();
2351 XMLNode &after = alist->get_state();
2352 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2355 _editor->commit_reversible_command ();
2359 FadeInDrag::aborted (bool)
2361 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2362 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2368 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2369 tmp->hide_fade_line();
2373 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2374 : RegionDrag (e, i, p, v)
2376 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2380 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2382 Drag::start_grab (event, cursor);
2384 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2385 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2387 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2389 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2393 FadeOutDrag::setup_pointer_frame_offset ()
2395 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2396 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2397 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2401 FadeOutDrag::motion (GdkEvent* event, bool)
2403 framecnt_t fade_length;
2405 framepos_t const pos = adjusted_current_frame (event);
2407 boost::shared_ptr<Region> region = _primary->region ();
2409 if (pos > (region->last_frame() - 64)) {
2410 fade_length = 64; // this should really be a minimum fade defined somewhere
2412 else if (pos < region->position()) {
2413 fade_length = region->length();
2416 fade_length = region->last_frame() - pos;
2419 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2421 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2427 tmp->reset_fade_out_shape_width (fade_length);
2428 tmp->show_fade_line(region->length() - fade_length);
2431 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2435 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2437 if (!movement_occurred) {
2441 framecnt_t fade_length;
2443 framepos_t const pos = adjusted_current_frame (event);
2445 boost::shared_ptr<Region> region = _primary->region ();
2447 if (pos > (region->last_frame() - 64)) {
2448 fade_length = 64; // this should really be a minimum fade defined somewhere
2450 else if (pos < region->position()) {
2451 fade_length = region->length();
2454 fade_length = region->last_frame() - pos;
2457 _editor->begin_reversible_command (_("change fade out length"));
2459 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2461 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2467 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2468 XMLNode &before = alist->get_state();
2470 tmp->audio_region()->set_fade_out_length (fade_length);
2471 tmp->audio_region()->set_fade_out_active (true);
2472 tmp->hide_fade_line();
2474 XMLNode &after = alist->get_state();
2475 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2478 _editor->commit_reversible_command ();
2482 FadeOutDrag::aborted (bool)
2484 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2485 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2491 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2492 tmp->hide_fade_line();
2496 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2499 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2501 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2504 _points.push_back (Gnome::Art::Point (0, 0));
2505 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2508 MarkerDrag::~MarkerDrag ()
2510 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2516 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2518 Drag::start_grab (event, cursor);
2522 Location *location = _editor->find_location_from_marker (_marker, is_start);
2523 _editor->_dragging_edit_point = true;
2525 update_item (location);
2527 // _drag_line->show();
2528 // _line->raise_to_top();
2531 show_verbose_cursor_time (location->start());
2533 show_verbose_cursor_time (location->end());
2536 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2539 case Selection::Toggle:
2540 _editor->selection->toggle (_marker);
2542 case Selection::Set:
2543 if (!_editor->selection->selected (_marker)) {
2544 _editor->selection->set (_marker);
2547 case Selection::Extend:
2549 Locations::LocationList ll;
2550 list<Marker*> to_add;
2552 _editor->selection->markers.range (s, e);
2553 s = min (_marker->position(), s);
2554 e = max (_marker->position(), e);
2557 if (e < max_framepos) {
2560 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2561 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2562 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2565 to_add.push_back (lm->start);
2568 to_add.push_back (lm->end);
2572 if (!to_add.empty()) {
2573 _editor->selection->add (to_add);
2577 case Selection::Add:
2578 _editor->selection->add (_marker);
2582 /* Set up copies for us to manipulate during the drag */
2584 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2585 Location* l = _editor->find_location_from_marker (*i, is_start);
2586 _copied_locations.push_back (new Location (*l));
2591 MarkerDrag::setup_pointer_frame_offset ()
2594 Location *location = _editor->find_location_from_marker (_marker, is_start);
2595 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2599 MarkerDrag::motion (GdkEvent* event, bool)
2601 framecnt_t f_delta = 0;
2603 bool move_both = false;
2605 Location *real_location;
2606 Location *copy_location = 0;
2608 framepos_t const newframe = adjusted_current_frame (event);
2610 framepos_t next = newframe;
2612 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2616 MarkerSelection::iterator i;
2617 list<Location*>::iterator x;
2619 /* find the marker we're dragging, and compute the delta */
2621 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2622 x != _copied_locations.end() && i != _editor->selection->markers.end();
2628 if (marker == _marker) {
2630 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2635 if (real_location->is_mark()) {
2636 f_delta = newframe - copy_location->start();
2640 switch (marker->type()) {
2641 case Marker::SessionStart:
2642 case Marker::RangeStart:
2643 case Marker::LoopStart:
2644 case Marker::PunchIn:
2645 f_delta = newframe - copy_location->start();
2648 case Marker::SessionEnd:
2649 case Marker::RangeEnd:
2650 case Marker::LoopEnd:
2651 case Marker::PunchOut:
2652 f_delta = newframe - copy_location->end();
2655 /* what kind of marker is this ? */
2663 if (i == _editor->selection->markers.end()) {
2664 /* hmm, impossible - we didn't find the dragged marker */
2668 /* now move them all */
2670 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2671 x != _copied_locations.end() && i != _editor->selection->markers.end();
2677 /* call this to find out if its the start or end */
2679 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2683 if (real_location->locked()) {
2687 if (copy_location->is_mark()) {
2691 copy_location->set_start (copy_location->start() + f_delta);
2695 framepos_t new_start = copy_location->start() + f_delta;
2696 framepos_t new_end = copy_location->end() + f_delta;
2698 if (is_start) { // start-of-range marker
2701 copy_location->set_start (new_start);
2702 copy_location->set_end (new_end);
2703 } else if (new_start < copy_location->end()) {
2704 copy_location->set_start (new_start);
2705 } else if (newframe > 0) {
2706 _editor->snap_to (next, 1, true);
2707 copy_location->set_end (next);
2708 copy_location->set_start (newframe);
2711 } else { // end marker
2714 copy_location->set_end (new_end);
2715 copy_location->set_start (new_start);
2716 } else if (new_end > copy_location->start()) {
2717 copy_location->set_end (new_end);
2718 } else if (newframe > 0) {
2719 _editor->snap_to (next, -1, true);
2720 copy_location->set_start (next);
2721 copy_location->set_end (newframe);
2726 update_item (copy_location);
2728 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2731 lm->set_position (copy_location->start(), copy_location->end());
2735 assert (!_copied_locations.empty());
2737 show_verbose_cursor_time (newframe);
2740 _editor->update_canvas_now ();
2745 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2747 if (!movement_occurred) {
2749 /* just a click, do nothing but finish
2750 off the selection process
2753 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2756 case Selection::Set:
2757 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2758 _editor->selection->set (_marker);
2762 case Selection::Toggle:
2763 case Selection::Extend:
2764 case Selection::Add:
2771 _editor->_dragging_edit_point = false;
2773 _editor->begin_reversible_command ( _("move marker") );
2774 XMLNode &before = _editor->session()->locations()->get_state();
2776 MarkerSelection::iterator i;
2777 list<Location*>::iterator x;
2780 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2781 x != _copied_locations.end() && i != _editor->selection->markers.end();
2784 Location * location = _editor->find_location_from_marker (*i, is_start);
2788 if (location->locked()) {
2792 if (location->is_mark()) {
2793 location->set_start ((*x)->start());
2795 location->set ((*x)->start(), (*x)->end());
2800 XMLNode &after = _editor->session()->locations()->get_state();
2801 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2802 _editor->commit_reversible_command ();
2806 MarkerDrag::aborted (bool)
2812 MarkerDrag::update_item (Location*)
2817 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2819 _cumulative_x_drag (0),
2820 _cumulative_y_drag (0)
2822 if (_zero_gain_fraction < 0.0) {
2823 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2826 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2828 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2834 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2836 Drag::start_grab (event, _editor->cursors()->fader);
2838 // start the grab at the center of the control point so
2839 // the point doesn't 'jump' to the mouse after the first drag
2840 _fixed_grab_x = _point->get_x();
2841 _fixed_grab_y = _point->get_y();
2843 float const fraction = 1 - (_point->get_y() / _point->line().height());
2845 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2847 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2848 event->button.x + 10, event->button.y + 10);
2850 _editor->verbose_cursor()->show ();
2854 ControlPointDrag::motion (GdkEvent* event, bool)
2856 double dx = _drags->current_pointer_x() - last_pointer_x();
2857 double dy = _drags->current_pointer_y() - last_pointer_y();
2859 if (event->button.state & Keyboard::SecondaryModifier) {
2864 /* coordinate in pixels relative to the start of the region (for region-based automation)
2865 or track (for track-based automation) */
2866 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2867 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2869 // calculate zero crossing point. back off by .01 to stay on the
2870 // positive side of zero
2871 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2873 // make sure we hit zero when passing through
2874 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2878 if (_x_constrained) {
2881 if (_y_constrained) {
2885 _cumulative_x_drag = cx - _fixed_grab_x;
2886 _cumulative_y_drag = cy - _fixed_grab_y;
2890 cy = min ((double) _point->line().height(), cy);
2892 framepos_t cx_frames = _editor->unit_to_frame (cx);
2894 if (!_x_constrained) {
2895 _editor->snap_to_with_modifier (cx_frames, event);
2898 cx_frames = min (cx_frames, _point->line().maximum_time());
2900 float const fraction = 1.0 - (cy / _point->line().height());
2902 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2904 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2906 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2910 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2912 if (!movement_occurred) {
2916 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2917 _editor->reset_point_selection ();
2921 motion (event, false);
2924 _point->line().end_drag ();
2925 _editor->session()->commit_reversible_command ();
2929 ControlPointDrag::aborted (bool)
2931 _point->line().reset ();
2935 ControlPointDrag::active (Editing::MouseMode m)
2937 if (m == Editing::MouseGain) {
2938 /* always active in mouse gain */
2942 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2943 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2946 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2949 _cumulative_y_drag (0)
2951 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2955 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2957 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2960 _item = &_line->grab_item ();
2962 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2963 origin, and ditto for y.
2966 double cx = event->button.x;
2967 double cy = event->button.y;
2969 _line->parent_group().w2i (cx, cy);
2971 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2976 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2977 /* no adjacent points */
2981 Drag::start_grab (event, _editor->cursors()->fader);
2983 /* store grab start in parent frame */
2988 double fraction = 1.0 - (cy / _line->height());
2990 _line->start_drag_line (before, after, fraction);
2992 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2993 event->button.x + 10, event->button.y + 10);
2995 _editor->verbose_cursor()->show ();
2999 LineDrag::motion (GdkEvent* event, bool)
3001 double dy = _drags->current_pointer_y() - last_pointer_y();
3003 if (event->button.state & Keyboard::SecondaryModifier) {
3007 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3009 _cumulative_y_drag = cy - _fixed_grab_y;
3012 cy = min ((double) _line->height(), cy);
3014 double const fraction = 1.0 - (cy / _line->height());
3018 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3024 /* we are ignoring x position for this drag, so we can just pass in anything */
3025 _line->drag_motion (0, fraction, true, push);
3027 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3031 LineDrag::finished (GdkEvent* event, bool)
3033 motion (event, false);
3035 _editor->session()->commit_reversible_command ();
3039 LineDrag::aborted (bool)
3044 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3047 _cumulative_x_drag (0)
3049 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3053 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3055 Drag::start_grab (event);
3057 _line = reinterpret_cast<Line*> (_item);
3060 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3062 double cx = event->button.x;
3063 double cy = event->button.y;
3065 _item->property_parent().get_value()->w2i(cx, cy);
3067 /* store grab start in parent frame */
3068 _region_view_grab_x = cx;
3070 _before = *(float*) _item->get_data ("position");
3072 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3074 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3078 FeatureLineDrag::motion (GdkEvent*, bool)
3080 double dx = _drags->current_pointer_x() - last_pointer_x();
3082 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3084 _cumulative_x_drag += dx;
3086 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3095 ArdourCanvas::Points points;
3097 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3099 _line->get_bounds(x1, y2, x2, y2);
3101 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3102 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3104 _line->property_points() = points;
3106 float *pos = new float;
3109 _line->set_data ("position", pos);
3115 FeatureLineDrag::finished (GdkEvent*, bool)
3117 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3118 _arv->update_transient(_before, _before);
3122 FeatureLineDrag::aborted (bool)
3127 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3130 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3134 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3136 Drag::start_grab (event);
3137 show_verbose_cursor_time (adjusted_current_frame (event));
3141 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3148 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3150 framepos_t grab = grab_frame ();
3151 if (Config->get_rubberbanding_snaps_to_grid ()) {
3152 _editor->snap_to_with_modifier (grab, event);
3155 /* base start and end on initial click position */
3165 if (_drags->current_pointer_y() < grab_y()) {
3166 y1 = _drags->current_pointer_y();
3169 y2 = _drags->current_pointer_y();
3174 if (start != end || y1 != y2) {
3176 double x1 = _editor->frame_to_pixel (start);
3177 double x2 = _editor->frame_to_pixel (end);
3179 _editor->rubberband_rect->property_x1() = x1;
3180 _editor->rubberband_rect->property_y1() = y1;
3181 _editor->rubberband_rect->property_x2() = x2;
3182 _editor->rubberband_rect->property_y2() = y2;
3184 _editor->rubberband_rect->show();
3185 _editor->rubberband_rect->raise_to_top();
3187 show_verbose_cursor_time (pf);
3189 do_select_things (event, true);
3194 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3199 if (grab_frame() < last_pointer_frame()) {
3201 x2 = last_pointer_frame ();
3204 x1 = last_pointer_frame ();
3210 if (_drags->current_pointer_y() < grab_y()) {
3211 y1 = _drags->current_pointer_y();
3214 y2 = _drags->current_pointer_y();
3218 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3222 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3224 if (movement_occurred) {
3226 motion (event, false);
3227 do_select_things (event, false);
3235 _editor->rubberband_rect->hide();
3239 RubberbandSelectDrag::aborted (bool)
3241 _editor->rubberband_rect->hide ();
3244 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3245 : RegionDrag (e, i, p, v)
3247 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3251 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3253 Drag::start_grab (event, cursor);
3255 show_verbose_cursor_time (adjusted_current_frame (event));
3259 TimeFXDrag::motion (GdkEvent* event, bool)
3261 RegionView* rv = _primary;
3263 framepos_t const pf = adjusted_current_frame (event);
3265 if (pf > rv->region()->position()) {
3266 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3269 show_verbose_cursor_time (pf);
3273 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3275 _primary->get_time_axis_view().hide_timestretch ();
3277 if (!movement_occurred) {
3281 if (last_pointer_frame() < _primary->region()->position()) {
3282 /* backwards drag of the left edge - not usable */
3286 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3288 float percentage = (double) newlen / (double) _primary->region()->length();
3290 #ifndef USE_RUBBERBAND
3291 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3292 if (_primary->region()->data_type() == DataType::AUDIO) {
3293 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3297 // XXX how do timeFX on multiple regions ?
3302 if (_editor->time_stretch (rs, percentage) == -1) {
3303 error << _("An error occurred while executing time stretch operation") << endmsg;
3308 TimeFXDrag::aborted (bool)
3310 _primary->get_time_axis_view().hide_timestretch ();
3313 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3316 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3320 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3322 Drag::start_grab (event);
3326 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3328 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3332 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3334 if (movement_occurred && _editor->session()) {
3335 /* make sure we stop */
3336 _editor->session()->request_transport_speed (0.0);
3341 ScrubDrag::aborted (bool)
3346 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3350 , _original_pointer_time_axis (-1)
3351 , _last_pointer_time_axis (-1)
3353 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3357 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3359 if (_editor->session() == 0) {
3363 Gdk::Cursor* cursor = 0;
3365 switch (_operation) {
3366 case CreateSelection:
3367 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3372 cursor = _editor->cursors()->selector;
3373 Drag::start_grab (event, cursor);
3376 case SelectionStartTrim:
3377 if (_editor->clicked_axisview) {
3378 _editor->clicked_axisview->order_selection_trims (_item, true);
3380 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3383 case SelectionEndTrim:
3384 if (_editor->clicked_axisview) {
3385 _editor->clicked_axisview->order_selection_trims (_item, false);
3387 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3391 Drag::start_grab (event, cursor);
3395 if (_operation == SelectionMove) {
3396 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3398 show_verbose_cursor_time (adjusted_current_frame (event));
3401 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3405 SelectionDrag::setup_pointer_frame_offset ()
3407 switch (_operation) {
3408 case CreateSelection:
3409 _pointer_frame_offset = 0;
3412 case SelectionStartTrim:
3414 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3417 case SelectionEndTrim:
3418 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3424 SelectionDrag::motion (GdkEvent* event, bool first_move)
3426 framepos_t start = 0;
3430 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3431 if (pending_time_axis.first == 0) {
3435 framepos_t const pending_position = adjusted_current_frame (event);
3437 /* only alter selection if things have changed */
3439 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3443 switch (_operation) {
3444 case CreateSelection:
3446 framepos_t grab = grab_frame ();
3449 _editor->snap_to (grab);
3452 if (pending_position < grab_frame()) {
3453 start = pending_position;
3456 end = pending_position;
3460 /* first drag: Either add to the selection
3461 or create a new selection
3467 /* adding to the selection */
3468 _editor->set_selected_track_as_side_effect (Selection::Add);
3469 //_editor->selection->add (_editor->clicked_axisview);
3470 _editor->clicked_selection = _editor->selection->add (start, end);
3475 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3476 //_editor->selection->set (_editor->clicked_axisview);
3477 _editor->set_selected_track_as_side_effect (Selection::Set);
3480 _editor->clicked_selection = _editor->selection->set (start, end);
3484 /* select the track that we're in */
3485 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3486 // _editor->set_selected_track_as_side_effect (Selection::Add);
3487 _editor->selection->add (pending_time_axis.first);
3488 _added_time_axes.push_back (pending_time_axis.first);
3491 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3492 tracks that we selected in the first place.
3495 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3496 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3498 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3499 while (i != _added_time_axes.end()) {
3501 list<TimeAxisView*>::iterator tmp = i;
3504 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3505 _editor->selection->remove (*i);
3506 _added_time_axes.remove (*i);
3515 case SelectionStartTrim:
3517 start = _editor->selection->time[_editor->clicked_selection].start;
3518 end = _editor->selection->time[_editor->clicked_selection].end;
3520 if (pending_position > end) {
3523 start = pending_position;
3527 case SelectionEndTrim:
3529 start = _editor->selection->time[_editor->clicked_selection].start;
3530 end = _editor->selection->time[_editor->clicked_selection].end;
3532 if (pending_position < start) {
3535 end = pending_position;
3542 start = _editor->selection->time[_editor->clicked_selection].start;
3543 end = _editor->selection->time[_editor->clicked_selection].end;
3545 length = end - start;
3547 start = pending_position;
3548 _editor->snap_to (start);
3550 end = start + length;
3555 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3556 _editor->start_canvas_autoscroll (1, 0);
3560 _editor->selection->replace (_editor->clicked_selection, start, end);
3563 if (_operation == SelectionMove) {
3564 show_verbose_cursor_time(start);
3566 show_verbose_cursor_time(pending_position);
3571 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3573 Session* s = _editor->session();
3575 if (movement_occurred) {
3576 motion (event, false);
3577 /* XXX this is not object-oriented programming at all. ick */
3578 if (_editor->selection->time.consolidate()) {
3579 _editor->selection->TimeChanged ();
3582 /* XXX what if its a music time selection? */
3583 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3584 s->request_play_range (&_editor->selection->time, true);
3589 /* just a click, no pointer movement.*/
3591 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3592 _editor->selection->clear_time();
3595 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3596 _editor->selection->set (_editor->clicked_axisview);
3599 if (s && s->get_play_range () && s->transport_rolling()) {
3600 s->request_stop (false, false);
3605 _editor->stop_canvas_autoscroll ();
3609 SelectionDrag::aborted (bool)
3614 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3619 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3621 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3622 physical_screen_height (_editor->get_window()));
3623 _drag_rect->hide ();
3625 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3626 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3630 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3632 if (_editor->session() == 0) {
3636 Gdk::Cursor* cursor = 0;
3638 if (!_editor->temp_location) {
3639 _editor->temp_location = new Location (*_editor->session());
3642 switch (_operation) {
3643 case CreateRangeMarker:
3644 case CreateTransportMarker:
3645 case CreateCDMarker:
3647 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3652 cursor = _editor->cursors()->selector;
3656 Drag::start_grab (event, cursor);
3658 show_verbose_cursor_time (adjusted_current_frame (event));
3662 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3664 framepos_t start = 0;
3666 ArdourCanvas::SimpleRect *crect;
3668 switch (_operation) {
3669 case CreateRangeMarker:
3670 crect = _editor->range_bar_drag_rect;
3672 case CreateTransportMarker:
3673 crect = _editor->transport_bar_drag_rect;
3675 case CreateCDMarker:
3676 crect = _editor->cd_marker_bar_drag_rect;
3679 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3684 framepos_t const pf = adjusted_current_frame (event);
3686 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3687 framepos_t grab = grab_frame ();
3688 _editor->snap_to (grab);
3690 if (pf < grab_frame()) {
3698 /* first drag: Either add to the selection
3699 or create a new selection.
3704 _editor->temp_location->set (start, end);
3708 update_item (_editor->temp_location);
3710 //_drag_rect->raise_to_top();
3715 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3716 _editor->start_canvas_autoscroll (1, 0);
3720 _editor->temp_location->set (start, end);
3722 double x1 = _editor->frame_to_pixel (start);
3723 double x2 = _editor->frame_to_pixel (end);
3724 crect->property_x1() = x1;
3725 crect->property_x2() = x2;
3727 update_item (_editor->temp_location);
3730 show_verbose_cursor_time (pf);
3735 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3737 Location * newloc = 0;
3741 if (movement_occurred) {
3742 motion (event, false);
3745 switch (_operation) {
3746 case CreateRangeMarker:
3747 case CreateCDMarker:
3749 _editor->begin_reversible_command (_("new range marker"));
3750 XMLNode &before = _editor->session()->locations()->get_state();
3751 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3752 if (_operation == CreateCDMarker) {
3753 flags = Location::IsRangeMarker | Location::IsCDMarker;
3754 _editor->cd_marker_bar_drag_rect->hide();
3757 flags = Location::IsRangeMarker;
3758 _editor->range_bar_drag_rect->hide();
3760 newloc = new Location (
3761 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3764 _editor->session()->locations()->add (newloc, true);
3765 XMLNode &after = _editor->session()->locations()->get_state();
3766 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3767 _editor->commit_reversible_command ();
3771 case CreateTransportMarker:
3772 // popup menu to pick loop or punch
3773 _editor->new_transport_marker_context_menu (&event->button, _item);
3777 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3779 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3784 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3786 if (end == max_framepos) {
3787 end = _editor->session()->current_end_frame ();
3790 if (start == max_framepos) {
3791 start = _editor->session()->current_start_frame ();
3794 switch (_editor->mouse_mode) {
3796 /* find the two markers on either side and then make the selection from it */
3797 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3801 /* find the two markers on either side of the click and make the range out of it */
3802 _editor->selection->set (start, end);
3811 _editor->stop_canvas_autoscroll ();
3815 RangeMarkerBarDrag::aborted (bool)
3821 RangeMarkerBarDrag::update_item (Location* location)
3823 double const x1 = _editor->frame_to_pixel (location->start());
3824 double const x2 = _editor->frame_to_pixel (location->end());
3826 _drag_rect->property_x1() = x1;
3827 _drag_rect->property_x2() = x2;
3830 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3834 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3838 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3840 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3841 Drag::start_grab (event, _editor->cursors()->zoom_out);
3844 Drag::start_grab (event, _editor->cursors()->zoom_in);
3848 show_verbose_cursor_time (adjusted_current_frame (event));
3852 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3857 framepos_t const pf = adjusted_current_frame (event);
3859 framepos_t grab = grab_frame ();
3860 _editor->snap_to_with_modifier (grab, event);
3862 /* base start and end on initial click position */
3874 _editor->zoom_rect->show();
3875 _editor->zoom_rect->raise_to_top();
3878 _editor->reposition_zoom_rect(start, end);
3880 show_verbose_cursor_time (pf);
3885 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3887 if (movement_occurred) {
3888 motion (event, false);
3890 if (grab_frame() < last_pointer_frame()) {
3891 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3893 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3896 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3897 _editor->tav_zoom_step (_zoom_out);
3899 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3903 _editor->zoom_rect->hide();
3907 MouseZoomDrag::aborted (bool)
3909 _editor->zoom_rect->hide ();
3912 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3914 , _cumulative_dx (0)
3915 , _cumulative_dy (0)
3917 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3919 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3920 _region = &_primary->region_view ();
3921 _note_height = _region->midi_stream_view()->note_height ();
3925 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3927 Drag::start_grab (event);
3929 if (!(_was_selected = _primary->selected())) {
3931 /* tertiary-click means extend selection - we'll do that on button release,
3932 so don't add it here, because otherwise we make it hard to figure
3933 out the "extend-to" range.
3936 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3939 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3942 _region->note_selected (_primary, true);
3944 _region->unique_select (_primary);
3950 /** @return Current total drag x change in frames */
3952 NoteDrag::total_dx () const
3955 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3957 /* primary note time */
3958 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3960 /* new time of the primary note in session frames */
3961 frameoffset_t st = n + dx;
3963 framepos_t const rp = _region->region()->position ();
3965 /* prevent the note being dragged earlier than the region's position */
3968 /* snap and return corresponding delta */
3969 return _region->snap_frame_to_frame (st - rp) + rp - n;
3972 /** @return Current total drag y change in note number */
3974 NoteDrag::total_dy () const
3976 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3980 NoteDrag::motion (GdkEvent *, bool)
3982 /* Total change in x and y since the start of the drag */
3983 frameoffset_t const dx = total_dx ();
3984 int8_t const dy = total_dy ();
3986 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3987 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3988 double const tdy = -dy * _note_height - _cumulative_dy;
3991 _cumulative_dx += tdx;
3992 _cumulative_dy += tdy;
3994 int8_t note_delta = total_dy();
3996 _region->move_selection (tdx, tdy, note_delta);
3998 /* the new note value may be the same as the old one, but we
3999 * don't know what that means because the selection may have
4000 * involved more than one note and we might be doing something
4001 * odd with them. so show the note value anyway, always.
4005 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4007 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4008 (int) floor (new_note));
4010 show_verbose_cursor_text (buf);
4015 NoteDrag::finished (GdkEvent* ev, bool moved)
4018 if (_editor->current_mouse_mode() == Editing::MouseObject) {
4020 if (_was_selected) {
4021 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4023 _region->note_deselected (_primary);
4026 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4027 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4029 if (!extend && !add && _region->selection_size() > 1) {
4030 _region->unique_select (_primary);
4031 } else if (extend) {
4032 _region->note_selected (_primary, true, true);
4034 /* it was added during button press */
4039 _region->note_dropped (_primary, total_dx(), total_dy());
4044 NoteDrag::aborted (bool)
4049 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
4050 : Drag (editor, item)
4052 , _nothing_to_drag (false)
4054 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4056 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
4059 /* get all lines in the automation view */
4060 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
4062 /* find those that overlap the ranges being dragged */
4063 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
4064 while (i != lines.end ()) {
4065 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4068 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4070 /* check this range against all the AudioRanges that we are using */
4071 list<AudioRange>::const_iterator k = _ranges.begin ();
4072 while (k != _ranges.end()) {
4073 if (k->coverage (r.first, r.second) != OverlapNone) {
4079 /* add it to our list if it overlaps at all */
4080 if (k != _ranges.end()) {
4085 _lines.push_back (n);
4091 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4095 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4097 Drag::start_grab (event, cursor);
4099 /* Get line states before we start changing things */
4100 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4101 i->state = &i->line->get_state ();
4104 if (_ranges.empty()) {
4106 /* No selected time ranges: drag all points */
4107 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4108 uint32_t const N = i->line->npoints ();
4109 for (uint32_t j = 0; j < N; ++j) {
4110 i->points.push_back (i->line->nth (j));
4116 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4118 framecnt_t const half = (i->start + i->end) / 2;
4120 /* find the line that this audio range starts in */
4121 list<Line>::iterator j = _lines.begin();
4122 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4126 if (j != _lines.end()) {
4127 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4129 /* j is the line that this audio range starts in; fade into it;
4130 64 samples length plucked out of thin air.
4133 framepos_t a = i->start + 64;
4138 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4139 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4141 the_list->add (p, the_list->eval (p));
4142 j->line->add_always_in_view (p);
4143 the_list->add (q, the_list->eval (q));
4144 j->line->add_always_in_view (q);
4147 /* same thing for the end */
4150 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4154 if (j != _lines.end()) {
4155 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4157 /* j is the line that this audio range starts in; fade out of it;
4158 64 samples length plucked out of thin air.
4161 framepos_t b = i->end - 64;
4166 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4167 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4169 the_list->add (p, the_list->eval (p));
4170 j->line->add_always_in_view (p);
4171 the_list->add (q, the_list->eval (q));
4172 j->line->add_always_in_view (q);
4176 _nothing_to_drag = true;
4178 /* Find all the points that should be dragged and put them in the relevant
4179 points lists in the Line structs.
4182 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4184 uint32_t const N = i->line->npoints ();
4185 for (uint32_t j = 0; j < N; ++j) {
4187 /* here's a control point on this line */
4188 ControlPoint* p = i->line->nth (j);
4189 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4191 /* see if it's inside a range */
4192 list<AudioRange>::const_iterator k = _ranges.begin ();
4193 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4197 if (k != _ranges.end()) {
4198 /* dragging this point */
4199 _nothing_to_drag = false;
4200 i->points.push_back (p);
4206 if (_nothing_to_drag) {
4210 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4211 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4216 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4218 if (_nothing_to_drag) {
4222 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4223 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4225 /* we are ignoring x position for this drag, so we can just pass in anything */
4226 i->line->drag_motion (0, f, true, false);
4231 AutomationRangeDrag::finished (GdkEvent* event, bool)
4233 if (_nothing_to_drag) {
4237 motion (event, false);
4238 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4239 i->line->end_drag ();
4240 i->line->clear_always_in_view ();
4243 _editor->session()->commit_reversible_command ();
4247 AutomationRangeDrag::aborted (bool)
4249 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4250 i->line->clear_always_in_view ();
4255 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4258 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4259 layer = v->region()->layer ();
4260 initial_y = v->get_canvas_group()->property_y ();
4261 initial_playlist = v->region()->playlist ();
4262 initial_position = v->region()->position ();
4263 initial_end = v->region()->position () + v->region()->length ();
4266 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4270 , _cumulative_dx (0)
4272 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4276 PatchChangeDrag::motion (GdkEvent* ev, bool)
4278 framepos_t f = adjusted_current_frame (ev);
4279 boost::shared_ptr<Region> r = _region_view->region ();
4280 f = max (f, r->position ());
4281 f = min (f, r->last_frame ());
4283 framecnt_t const dxf = f - grab_frame();
4284 double const dxu = _editor->frame_to_unit (dxf);
4285 _patch_change->move (dxu - _cumulative_dx, 0);
4286 _cumulative_dx = dxu;
4290 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4292 if (!movement_occurred) {
4296 boost::shared_ptr<Region> r (_region_view->region ());
4298 framepos_t f = adjusted_current_frame (ev);
4299 f = max (f, r->position ());
4300 f = min (f, r->last_frame ());
4302 _region_view->move_patch_change (
4304 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4309 PatchChangeDrag::aborted (bool)
4311 _patch_change->move (-_cumulative_dx, 0);
4315 PatchChangeDrag::setup_pointer_frame_offset ()
4317 boost::shared_ptr<Region> region = _region_view->region ();
4318 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4321 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4322 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4329 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4331 framepos_t const p = _region_view->region()->position ();
4332 double const y = _region_view->midi_view()->y_position ();
4334 x1 = max ((framepos_t) 0, x1 - p);
4335 x2 = max ((framepos_t) 0, x2 - p);
4336 y1 = max (0.0, y1 - y);
4337 y2 = max (0.0, y2 - y);
4339 _region_view->update_drag_selection (
4340 _editor->frame_to_pixel (x1),
4341 _editor->frame_to_pixel (x2),
4344 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4349 MidiRubberbandSelectDrag::deselect_things ()
4354 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4355 : RubberbandSelectDrag (e, i)
4361 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4363 if (drag_in_progress) {
4364 /* We just want to select things at the end of the drag, not during it */
4368 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4370 _editor->begin_reversible_command (_("rubberband selection"));
4371 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4372 _editor->commit_reversible_command ();
4376 EditorRubberbandSelectDrag::deselect_things ()
4378 if (!getenv("ARDOUR_SAE")) {
4379 _editor->selection->clear_tracks();
4381 _editor->selection->clear_regions();
4382 _editor->selection->clear_points ();
4383 _editor->selection->clear_lines ();
4386 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4394 NoteCreateDrag::~NoteCreateDrag ()
4400 NoteCreateDrag::grid_frames (framepos_t t) const
4403 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4408 return _region_view->region_beats_to_region_frames (grid_beats);
4412 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4414 Drag::start_grab (event, cursor);
4416 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4418 framepos_t pf = _drags->current_pointer_frame ();
4419 framecnt_t const g = grid_frames (pf);
4421 /* Hack so that we always snap to the note that we are over, instead of snapping
4422 to the next one if we're more than halfway through the one we're over.
4424 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4428 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4430 MidiStreamView* sv = _region_view->midi_stream_view ();
4431 double const x = _editor->frame_to_pixel (_note[0]);
4432 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4434 _drag_rect->property_x1() = x;
4435 _drag_rect->property_y1() = y;
4436 _drag_rect->property_x2() = x;
4437 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4439 _drag_rect->property_outline_what() = 0xff;
4440 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4441 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4445 NoteCreateDrag::motion (GdkEvent* event, bool)
4447 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4448 double const x = _editor->frame_to_pixel (_note[1]);
4449 if (_note[1] > _note[0]) {
4450 _drag_rect->property_x2() = x;
4452 _drag_rect->property_x1() = x;
4457 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4459 if (!had_movement) {
4463 framepos_t const start = min (_note[0], _note[1]);
4464 framecnt_t length = abs (_note[0] - _note[1]);
4466 framecnt_t const g = grid_frames (start);
4467 if (_editor->snap_mode() == SnapNormal && length < g) {
4471 _region_view->create_note_at (start, _drag_rect->property_y1(), _region_view->region_frames_to_region_beats (length), true, false);
4475 NoteCreateDrag::y_to_region (double y) const
4478 _region_view->get_canvas_group()->w2i (x, y);
4483 NoteCreateDrag::aborted (bool)