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;
494 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
496 /* compute the amount of pointer motion in frames, and where
497 the region would be if we moved it by that much.
499 *pending_region_position = adjusted_current_frame (event);
501 framepos_t sync_frame;
502 framecnt_t sync_offset;
505 sync_offset = _primary->region()->sync_offset (sync_dir);
507 /* we don't handle a sync point that lies before zero.
509 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
511 sync_frame = *pending_region_position + (sync_dir*sync_offset);
513 _editor->snap_to_with_modifier (sync_frame, event);
515 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
518 *pending_region_position = _last_frame_position;
521 if (*pending_region_position > max_framepos - _primary->region()->length()) {
522 *pending_region_position = _last_frame_position;
527 /* in locked edit mode, reverse the usual meaning of _x_constrained */
528 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
530 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
532 /* x movement since last time (in pixels) */
533 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
535 /* total x movement */
536 framecnt_t total_dx = *pending_region_position;
537 if (regions_came_from_canvas()) {
538 total_dx = total_dx - grab_frame ();
541 /* check that no regions have gone off the start of the session */
542 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
543 if ((i->view->region()->position() + total_dx) < 0) {
545 *pending_region_position = _last_frame_position;
550 _last_frame_position = *pending_region_position;
557 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
559 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
560 int const n = i->time_axis_view + delta_track;
561 if (n < 0 || n >= int (_time_axis_views.size())) {
562 /* off the top or bottom track */
566 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
567 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
568 /* not a track, or the wrong type */
572 double const l = i->layer + delta_layer;
574 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
575 mode to allow the user to place a region below another on layer 0.
577 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
578 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
579 If it has, the layers will be munged later anyway, so it's ok.
585 /* all regions being dragged are ok with this change */
590 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
592 assert (!_views.empty ());
594 /* Find the TimeAxisView that the pointer is now over */
595 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
597 if (first_move && tv.first->view()->layer_display() == Stacked) {
598 tv.first->view()->set_layer_display (Expanded);
601 /* Bail early if we're not over a track */
602 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
603 if (!rtv || !rtv->is_track()) {
604 _editor->verbose_cursor()->hide ();
608 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
610 /* Here's the current pointer position in terms of time axis view and layer */
611 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
612 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
614 /* Work out the change in x */
615 framepos_t pending_region_position;
616 double const x_delta = compute_x_delta (event, &pending_region_position);
618 /* Work out the change in y */
619 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
620 double delta_layer = current_pointer_layer - _last_pointer_layer;
622 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
623 /* this y movement is not allowed, so do no y movement this time */
624 delta_time_axis_view = 0;
628 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
629 /* haven't reached next snap point, and we're not switching
630 trackviews nor layers. nothing to do.
635 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
637 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
639 RegionView* rv = i->view;
641 if (rv->region()->locked()) {
647 rv->get_time_axis_view().hide_dependent_views (*rv);
649 /* Absolutely no idea why this is necessary, but it is; without
650 it, the region view disappears after the reparent.
652 _editor->update_canvas_now ();
654 /* Reparent to a non scrolling group so that we can keep the
655 region selection above all time axis views.
656 Reparenting means that we will have to move the region view
657 later, as the two parent groups have different coordinates.
660 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
662 rv->fake_set_opaque (true);
665 /* If we have moved tracks, we'll fudge the layer delta so that the
666 region gets moved back onto layer 0 on its new track; this avoids
667 confusion when dragging regions from non-zero layers onto different
670 double this_delta_layer = delta_layer;
671 if (delta_time_axis_view != 0) {
672 this_delta_layer = - i->layer;
675 /* The TimeAxisView that this region is now on */
676 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
678 /* Ensure it is moved from stacked -> expanded if appropriate */
679 if (tv->view()->layer_display() == Stacked) {
680 tv->view()->set_layer_display (Expanded);
683 /* We're only allowed to go -ve in layer on Expanded views */
684 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
685 this_delta_layer = - i->layer;
689 rv->set_height (tv->view()->child_height ());
691 /* Update show/hidden status as the region view may have come from a hidden track,
692 or have moved to one.
695 rv->get_canvas_group()->hide ();
697 rv->get_canvas_group()->show ();
700 /* Update the DraggingView */
701 i->time_axis_view += delta_time_axis_view;
702 i->layer += this_delta_layer;
705 _editor->mouse_brush_insert_region (rv, pending_region_position);
710 /* Get the y coordinate of the top of the track that this region is now on */
711 tv->canvas_display()->i2w (x, y);
712 y += _editor->get_trackview_group_vertical_offset();
714 /* And adjust for the layer that it should be on */
715 StreamView* cv = tv->view ();
716 switch (cv->layer_display ()) {
720 y += (cv->layers() - i->layer - 1) * cv->child_height ();
723 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
727 /* Now move the region view */
728 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
731 } /* foreach region */
733 _total_x_delta += x_delta;
736 _editor->cursor_group->raise_to_top();
739 if (x_delta != 0 && !_brushing) {
740 show_verbose_cursor_time (_last_frame_position);
743 _last_pointer_time_axis_view += delta_time_axis_view;
744 _last_pointer_layer += delta_layer;
748 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
750 if (_copy && first_move) {
752 /* duplicate the regionview(s) and region(s) */
754 list<DraggingView> new_regionviews;
756 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
758 RegionView* rv = i->view;
759 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
760 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
762 const boost::shared_ptr<const Region> original = rv->region();
763 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
764 region_copy->set_position (original->position());
768 boost::shared_ptr<AudioRegion> audioregion_copy
769 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
771 nrv = new AudioRegionView (*arv, audioregion_copy);
773 boost::shared_ptr<MidiRegion> midiregion_copy
774 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
775 nrv = new MidiRegionView (*mrv, midiregion_copy);
780 nrv->get_canvas_group()->show ();
781 new_regionviews.push_back (DraggingView (nrv, this));
783 /* swap _primary to the copy */
785 if (rv == _primary) {
789 /* ..and deselect the one we copied */
791 rv->set_selected (false);
794 if (!new_regionviews.empty()) {
796 /* reflect the fact that we are dragging the copies */
798 _views = new_regionviews;
800 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
803 sync the canvas to what we think is its current state
804 without it, the canvas seems to
805 "forget" to update properly after the upcoming reparent()
806 ..only if the mouse is in rapid motion at the time of the grab.
807 something to do with regionview creation taking so long?
809 _editor->update_canvas_now();
813 RegionMotionDrag::motion (event, first_move);
817 RegionMotionDrag::finished (GdkEvent *, bool)
819 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
824 if ((*i)->view()->layer_display() == Expanded) {
825 (*i)->view()->set_layer_display (Stacked);
831 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
833 RegionMotionDrag::finished (ev, movement_occurred);
835 if (!movement_occurred) {
840 /* reverse this here so that we have the correct logic to finalize
844 if (Config->get_edit_mode() == Lock) {
845 _x_constrained = !_x_constrained;
848 assert (!_views.empty ());
850 /* We might have hidden region views so that they weren't visible during the drag
851 (when they have been reparented). Now everything can be shown again, as region
852 views are back in their track parent groups.
854 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
855 i->view->get_canvas_group()->show ();
858 bool const changed_position = (_last_frame_position != _primary->region()->position());
859 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
860 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
862 _editor->update_canvas_now ();
884 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
886 RegionSelection new_views;
887 PlaylistSet modified_playlists;
888 list<RegionView*> views_to_delete;
891 /* all changes were made during motion event handlers */
893 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
897 _editor->commit_reversible_command ();
901 if (_x_constrained) {
902 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
904 _editor->begin_reversible_command (Operations::region_copy);
907 /* insert the regions into their new playlists */
908 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
910 if (i->view->region()->locked()) {
916 if (changed_position && !_x_constrained) {
917 where = i->view->region()->position() - drag_delta;
919 where = i->view->region()->position();
922 RegionView* new_view = insert_region_into_playlist (
923 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
930 new_views.push_back (new_view);
932 /* we don't need the copied RegionView any more */
933 views_to_delete.push_back (i->view);
936 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
937 because when views are deleted they are automagically removed from _views, which messes
940 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
944 /* If we've created new regions either by copying or moving
945 to a new track, we want to replace the old selection with the new ones
948 if (new_views.size() > 0) {
949 _editor->selection->set (new_views);
952 /* write commands for the accumulated diffs for all our modified playlists */
953 add_stateful_diff_commands_for_playlists (modified_playlists);
955 _editor->commit_reversible_command ();
959 RegionMoveDrag::finished_no_copy (
960 bool const changed_position,
961 bool const changed_tracks,
962 framecnt_t const drag_delta
965 RegionSelection new_views;
966 PlaylistSet modified_playlists;
967 PlaylistSet frozen_playlists;
968 set<RouteTimeAxisView*> views_to_update;
971 /* all changes were made during motion event handlers */
972 _editor->commit_reversible_command ();
976 if (_x_constrained) {
977 _editor->begin_reversible_command (_("fixed time region drag"));
979 _editor->begin_reversible_command (Operations::region_drag);
982 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
984 RegionView* rv = i->view;
986 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
987 double const dest_layer = i->layer;
989 if (rv->region()->locked()) {
994 views_to_update.insert (dest_rtv);
998 if (changed_position && !_x_constrained) {
999 where = rv->region()->position() - drag_delta;
1001 where = rv->region()->position();
1004 if (changed_tracks) {
1006 /* insert into new playlist */
1008 RegionView* new_view = insert_region_into_playlist (
1009 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1012 if (new_view == 0) {
1017 new_views.push_back (new_view);
1019 /* remove from old playlist */
1021 /* the region that used to be in the old playlist is not
1022 moved to the new one - we use a copy of it. as a result,
1023 any existing editor for the region should no longer be
1026 rv->hide_region_editor();
1027 rv->fake_set_opaque (false);
1029 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1033 rv->region()->clear_changes ();
1036 motion on the same track. plonk the previously reparented region
1037 back to its original canvas group (its streamview).
1038 No need to do anything for copies as they are fake regions which will be deleted.
1041 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1042 rv->get_canvas_group()->property_y() = i->initial_y;
1043 rv->get_time_axis_view().reveal_dependent_views (*rv);
1045 /* just change the model */
1047 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1049 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1050 playlist->set_layer (rv->region(), dest_layer);
1053 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1055 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1058 playlist->freeze ();
1061 /* this movement may result in a crossfade being modified, so we need to get undo
1062 data from the playlist as well as the region.
1065 r = modified_playlists.insert (playlist);
1067 playlist->clear_changes ();
1070 rv->region()->set_position (where);
1072 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1075 if (changed_tracks) {
1077 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1078 was selected in all of them, then removing it from a playlist will have removed all
1079 trace of it from _views (i.e. there were N regions selected, we removed 1,
1080 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1081 corresponding regionview, and _views is now empty).
1083 This could have invalidated any and all iterators into _views.
1085 The heuristic we use here is: if the region selection is empty, break out of the loop
1086 here. if the region selection is not empty, then restart the loop because we know that
1087 we must have removed at least the region(view) we've just been working on as well as any
1088 that we processed on previous iterations.
1090 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1091 we can just iterate.
1095 if (_views.empty()) {
1106 /* If we've created new regions either by copying or moving
1107 to a new track, we want to replace the old selection with the new ones
1110 if (new_views.size() > 0) {
1111 _editor->selection->set (new_views);
1114 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1118 /* write commands for the accumulated diffs for all our modified playlists */
1119 add_stateful_diff_commands_for_playlists (modified_playlists);
1121 _editor->commit_reversible_command ();
1123 /* We have futzed with the layering of canvas items on our streamviews.
1124 If any region changed layer, this will have resulted in the stream
1125 views being asked to set up their region views, and all will be well.
1126 If not, we might now have badly-ordered region views. Ask the StreamViews
1127 involved to sort themselves out, just in case.
1130 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1131 (*i)->view()->playlist_layered ((*i)->track ());
1135 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1136 * @param region Region to remove.
1137 * @param playlist playlist To remove from.
1138 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1139 * that clear_changes () is only called once per playlist.
1142 RegionMoveDrag::remove_region_from_playlist (
1143 boost::shared_ptr<Region> region,
1144 boost::shared_ptr<Playlist> playlist,
1145 PlaylistSet& modified_playlists
1148 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1151 playlist->clear_changes ();
1154 playlist->remove_region (region);
1158 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1159 * clearing the playlist's diff history first if necessary.
1160 * @param region Region to insert.
1161 * @param dest_rtv Destination RouteTimeAxisView.
1162 * @param dest_layer Destination layer.
1163 * @param where Destination position.
1164 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1165 * that clear_changes () is only called once per playlist.
1166 * @return New RegionView, or 0 if no insert was performed.
1169 RegionMoveDrag::insert_region_into_playlist (
1170 boost::shared_ptr<Region> region,
1171 RouteTimeAxisView* dest_rtv,
1174 PlaylistSet& modified_playlists
1177 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1178 if (!dest_playlist) {
1182 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1183 _new_region_view = 0;
1184 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1186 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1187 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1189 dest_playlist->clear_changes ();
1192 dest_playlist->add_region (region, where);
1194 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1195 dest_playlist->set_layer (region, dest_layer);
1200 assert (_new_region_view);
1202 return _new_region_view;
1206 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1208 _new_region_view = rv;
1212 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1214 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1215 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1217 _editor->session()->add_command (c);
1226 RegionMoveDrag::aborted (bool movement_occurred)
1230 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1237 RegionMotionDrag::aborted (movement_occurred);
1242 RegionMotionDrag::aborted (bool)
1244 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1245 if ((*i)->view()->layer_display() == Expanded) {
1246 (*i)->view()->set_layer_display (Stacked);
1250 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1251 RegionView* rv = i->view;
1252 TimeAxisView* tv = &(rv->get_time_axis_view ());
1253 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1255 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1256 rv->get_canvas_group()->property_y() = 0;
1257 rv->get_time_axis_view().reveal_dependent_views (*rv);
1258 rv->fake_set_opaque (false);
1259 rv->move (-_total_x_delta, 0);
1260 rv->set_height (rtv->view()->child_height ());
1263 _editor->update_canvas_now ();
1266 /** @param b true to brush, otherwise false.
1267 * @param c true to make copies of the regions being moved, otherwise false.
1269 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1270 : RegionMotionDrag (e, i, p, v, b),
1273 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1276 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1277 if (rtv && rtv->is_track()) {
1278 speed = rtv->track()->speed ();
1281 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1285 RegionMoveDrag::setup_pointer_frame_offset ()
1287 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1290 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1291 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1293 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1295 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1296 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1298 _primary = v->view()->create_region_view (r, false, false);
1300 _primary->get_canvas_group()->show ();
1301 _primary->set_position (pos, 0);
1302 _views.push_back (DraggingView (_primary, this));
1304 _last_frame_position = pos;
1306 _item = _primary->get_canvas_group ();
1310 RegionInsertDrag::finished (GdkEvent *, bool)
1312 _editor->update_canvas_now ();
1314 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1316 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1317 _primary->get_canvas_group()->property_y() = 0;
1319 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1321 _editor->begin_reversible_command (Operations::insert_region);
1322 playlist->clear_changes ();
1323 playlist->add_region (_primary->region (), _last_frame_position);
1324 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1325 _editor->commit_reversible_command ();
1333 RegionInsertDrag::aborted (bool)
1340 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1341 : RegionMoveDrag (e, i, p, v, false, false)
1343 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1346 struct RegionSelectionByPosition {
1347 bool operator() (RegionView*a, RegionView* b) {
1348 return a->region()->position () < b->region()->position();
1353 RegionSpliceDrag::motion (GdkEvent* event, bool)
1355 /* Which trackview is this ? */
1357 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1358 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1360 /* The region motion is only processed if the pointer is over
1364 if (!tv || !tv->is_track()) {
1365 /* To make sure we hide the verbose canvas cursor when the mouse is
1366 not held over and audiotrack.
1368 _editor->verbose_cursor()->hide ();
1374 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1380 RegionSelection copy (_editor->selection->regions);
1382 RegionSelectionByPosition cmp;
1385 framepos_t const pf = adjusted_current_frame (event);
1387 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1389 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1395 boost::shared_ptr<Playlist> playlist;
1397 if ((playlist = atv->playlist()) == 0) {
1401 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1406 if (pf < (*i)->region()->last_frame() + 1) {
1410 if (pf > (*i)->region()->first_frame()) {
1416 playlist->shuffle ((*i)->region(), dir);
1421 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1423 RegionMoveDrag::finished (event, movement_occurred);
1427 RegionSpliceDrag::aborted (bool)
1432 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1434 _view (dynamic_cast<MidiTimeAxisView*> (v))
1436 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1442 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1446 _view->playlist()->freeze ();
1449 framepos_t const f = adjusted_current_frame (event);
1450 if (f < grab_frame()) {
1451 _region->set_position (f);
1454 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1455 so that if this region is duplicated, its duplicate starts on
1456 a snap point rather than 1 frame after a snap point. Otherwise things get
1457 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1458 place snapped notes at the start of the region.
1461 framecnt_t const len = abs (f - grab_frame () - 1);
1462 _region->set_length (len < 1 ? 1 : len);
1468 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1470 if (!movement_occurred) {
1473 _view->playlist()->thaw ();
1477 _editor->commit_reversible_command ();
1482 RegionCreateDrag::add_region ()
1484 if (_editor->session()) {
1485 const TempoMap& map (_editor->session()->tempo_map());
1486 framecnt_t pos = grab_frame();
1487 const Meter& m = map.meter_at (pos);
1488 /* not that the frame rate used here can be affected by pull up/down which
1491 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1492 _region = _view->add_region (grab_frame(), len, false);
1497 RegionCreateDrag::aborted (bool)
1500 _view->playlist()->thaw ();
1506 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1510 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1514 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1516 Gdk::Cursor* cursor;
1517 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1518 float x_fraction = cnote->mouse_x_fraction ();
1520 if (x_fraction > 0.0 && x_fraction < 0.25) {
1521 cursor = _editor->cursors()->left_side_trim;
1523 cursor = _editor->cursors()->right_side_trim;
1526 Drag::start_grab (event, cursor);
1528 region = &cnote->region_view();
1530 double const region_start = region->get_position_pixels();
1531 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1533 if (grab_x() <= middle_point) {
1534 cursor = _editor->cursors()->left_side_trim;
1537 cursor = _editor->cursors()->right_side_trim;
1541 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1543 if (event->motion.state & Keyboard::PrimaryModifier) {
1549 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1551 if (ms.size() > 1) {
1552 /* has to be relative, may make no sense otherwise */
1556 /* select this note; if it is already selected, preserve the existing selection,
1557 otherwise make this note the only one selected.
1559 region->note_selected (cnote, cnote->selected ());
1561 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1562 MidiRegionSelection::iterator next;
1565 (*r)->begin_resizing (at_front);
1571 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1573 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1574 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1575 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1580 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1582 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1583 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1584 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1589 NoteResizeDrag::aborted (bool)
1591 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1592 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1593 (*r)->abort_resizing ();
1597 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1600 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1604 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1610 RegionGainDrag::finished (GdkEvent *, bool)
1616 RegionGainDrag::aborted (bool)
1621 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1622 : RegionDrag (e, i, p, v)
1624 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1628 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1631 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1632 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1634 if (tv && tv->is_track()) {
1635 speed = tv->track()->speed();
1638 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1639 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1640 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1642 framepos_t const pf = adjusted_current_frame (event);
1644 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1645 /* Move the contents of the region around without changing the region bounds */
1646 _operation = ContentsTrim;
1647 Drag::start_grab (event, _editor->cursors()->trimmer);
1649 /* These will get overridden for a point trim.*/
1650 if (pf < (region_start + region_length/2)) {
1651 /* closer to front */
1652 _operation = StartTrim;
1653 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1656 _operation = EndTrim;
1657 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1661 switch (_operation) {
1663 show_verbose_cursor_time (region_start);
1664 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1665 i->view->trim_front_starting ();
1669 show_verbose_cursor_time (region_end);
1672 show_verbose_cursor_time (pf);
1676 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1677 i->view->region()->suspend_property_changes ();
1682 TrimDrag::motion (GdkEvent* event, bool first_move)
1684 RegionView* rv = _primary;
1687 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1688 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1689 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1691 if (tv && tv->is_track()) {
1692 speed = tv->track()->speed();
1695 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1701 switch (_operation) {
1703 trim_type = "Region start trim";
1706 trim_type = "Region end trim";
1709 trim_type = "Region content trim";
1713 _editor->begin_reversible_command (trim_type);
1715 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1716 RegionView* rv = i->view;
1717 rv->fake_set_opaque (false);
1718 rv->enable_display (false);
1719 rv->region()->playlist()->clear_owned_changes ();
1721 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1724 arv->temporarily_hide_envelope ();
1727 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1728 insert_result = _editor->motion_frozen_playlists.insert (pl);
1730 if (insert_result.second) {
1736 bool non_overlap_trim = false;
1738 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1739 non_overlap_trim = true;
1742 switch (_operation) {
1744 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1745 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1750 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1751 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1757 bool swap_direction = false;
1759 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1760 swap_direction = true;
1763 framecnt_t frame_delta = 0;
1765 bool left_direction = false;
1766 if (last_pointer_frame() > adjusted_current_frame(event)) {
1767 left_direction = true;
1770 if (left_direction) {
1771 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1773 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1776 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1777 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1783 switch (_operation) {
1785 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1788 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1791 show_verbose_cursor_time (adjusted_current_frame (event));
1798 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1800 if (movement_occurred) {
1801 motion (event, false);
1803 /* This must happen before the region's StatefulDiffCommand is created, as it may
1804 `correct' (ahem) the region's _start from being negative to being zero. It
1805 needs to be zero in the undo record.
1807 if (_operation == StartTrim) {
1808 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1809 i->view->trim_front_ending ();
1813 if (!_editor->selection->selected (_primary)) {
1814 _primary->thaw_after_trim ();
1817 set<boost::shared_ptr<Playlist> > diffed_playlists;
1819 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1820 i->view->thaw_after_trim ();
1821 i->view->enable_display (true);
1822 i->view->fake_set_opaque (true);
1824 /* Trimming one region may affect others on the playlist, so we need
1825 to get undo Commands from the whole playlist rather than just the
1826 region. Use diffed_playlists to make sure we don't diff a given
1827 playlist more than once.
1829 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1830 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1831 vector<Command*> cmds;
1833 _editor->session()->add_commands (cmds);
1834 diffed_playlists.insert (p);
1838 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1842 _editor->motion_frozen_playlists.clear ();
1843 _editor->commit_reversible_command();
1846 /* no mouse movement */
1847 _editor->point_trim (event, adjusted_current_frame (event));
1850 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1851 if (_operation == StartTrim) {
1852 i->view->trim_front_ending ();
1855 i->view->region()->resume_property_changes ();
1860 TrimDrag::aborted (bool movement_occurred)
1862 /* Our motion method is changing model state, so use the Undo system
1863 to cancel. Perhaps not ideal, as this will leave an Undo point
1864 behind which may be slightly odd from the user's point of view.
1869 if (movement_occurred) {
1873 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1874 i->view->region()->resume_property_changes ();
1879 TrimDrag::setup_pointer_frame_offset ()
1881 list<DraggingView>::iterator i = _views.begin ();
1882 while (i != _views.end() && i->view != _primary) {
1886 if (i == _views.end()) {
1890 switch (_operation) {
1892 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1895 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1902 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1906 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1907 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1912 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1914 Drag::start_grab (event, cursor);
1915 show_verbose_cursor_time (adjusted_current_frame(event));
1919 MeterMarkerDrag::setup_pointer_frame_offset ()
1921 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1925 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1929 // create a dummy marker for visual representation of moving the
1930 // section, because whether its a copy or not, we're going to
1931 // leave or lose the original marker (leave if its a copy; lose if its
1932 // not, because we'll remove it from the map).
1934 MeterSection section (_marker->meter());
1936 if (!section.movable()) {
1941 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1943 _marker = new MeterMarker (
1945 *_editor->meter_group,
1946 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1948 *new MeterSection (_marker->meter())
1951 /* use the new marker for the grab */
1952 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1955 TempoMap& map (_editor->session()->tempo_map());
1956 /* get current state */
1957 before_state = &map.get_state();
1958 /* remove the section while we drag it */
1959 map.remove_meter (section, true);
1963 framepos_t const pf = adjusted_current_frame (event);
1964 _marker->set_position (pf);
1965 show_verbose_cursor_time (pf);
1969 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1971 if (!movement_occurred) {
1975 motion (event, false);
1977 Timecode::BBT_Time when;
1979 TempoMap& map (_editor->session()->tempo_map());
1980 map.bbt_time (last_pointer_frame(), when);
1982 if (_copy == true) {
1983 _editor->begin_reversible_command (_("copy meter mark"));
1984 XMLNode &before = map.get_state();
1985 map.add_meter (_marker->meter(), when);
1986 XMLNode &after = map.get_state();
1987 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1988 _editor->commit_reversible_command ();
1991 _editor->begin_reversible_command (_("move meter mark"));
1993 /* we removed it before, so add it back now */
1995 map.add_meter (_marker->meter(), when);
1996 XMLNode &after = map.get_state();
1997 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
1998 _editor->commit_reversible_command ();
2001 // delete the dummy marker we used for visual representation while moving.
2002 // a new visual marker will show up automatically.
2007 MeterMarkerDrag::aborted (bool moved)
2009 _marker->set_position (_marker->meter().frame ());
2012 TempoMap& map (_editor->session()->tempo_map());
2013 /* we removed it before, so add it back now */
2014 map.add_meter (_marker->meter(), _marker->meter().frame());
2015 // delete the dummy marker we used for visual representation while moving.
2016 // a new visual marker will show up automatically.
2021 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2025 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2027 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2032 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2034 Drag::start_grab (event, cursor);
2035 show_verbose_cursor_time (adjusted_current_frame (event));
2039 TempoMarkerDrag::setup_pointer_frame_offset ()
2041 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2045 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2049 // create a dummy marker for visual representation of moving the
2050 // section, because whether its a copy or not, we're going to
2051 // leave or lose the original marker (leave if its a copy; lose if its
2052 // not, because we'll remove it from the map).
2054 // create a dummy marker for visual representation of moving the copy.
2055 // The actual copying is not done before we reach the finish callback.
2058 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2060 TempoSection section (_marker->tempo());
2062 _marker = new TempoMarker (
2064 *_editor->tempo_group,
2065 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2067 *new TempoSection (_marker->tempo())
2070 /* use the new marker for the grab */
2071 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2074 TempoMap& map (_editor->session()->tempo_map());
2075 /* get current state */
2076 before_state = &map.get_state();
2077 /* remove the section while we drag it */
2078 map.remove_tempo (section, true);
2082 framepos_t const pf = adjusted_current_frame (event);
2083 _marker->set_position (pf);
2084 show_verbose_cursor_time (pf);
2088 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2090 if (!movement_occurred) {
2094 motion (event, false);
2096 TempoMap& map (_editor->session()->tempo_map());
2097 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2098 Timecode::BBT_Time when;
2100 map.bbt_time (beat_time, when);
2102 if (_copy == true) {
2103 _editor->begin_reversible_command (_("copy tempo mark"));
2104 XMLNode &before = map.get_state();
2105 map.add_tempo (_marker->tempo(), when);
2106 XMLNode &after = map.get_state();
2107 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2108 _editor->commit_reversible_command ();
2111 _editor->begin_reversible_command (_("move tempo mark"));
2112 /* we removed it before, so add it back now */
2113 map.add_tempo (_marker->tempo(), when);
2114 XMLNode &after = map.get_state();
2115 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2116 _editor->commit_reversible_command ();
2119 // delete the dummy marker we used for visual representation while moving.
2120 // a new visual marker will show up automatically.
2125 TempoMarkerDrag::aborted (bool moved)
2127 _marker->set_position (_marker->tempo().frame());
2129 TempoMap& map (_editor->session()->tempo_map());
2130 /* we removed it before, so add it back now */
2131 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2132 // delete the dummy marker we used for visual representation while moving.
2133 // a new visual marker will show up automatically.
2138 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2142 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2145 /** Do all the things we do when dragging the playhead to make it look as though
2146 * we have located, without actually doing the locate (because that would cause
2147 * the diskstream buffers to be refilled, which is too slow).
2150 CursorDrag::fake_locate (framepos_t t)
2152 _editor->playhead_cursor->set_position (t);
2154 Session* s = _editor->session ();
2155 if (s->timecode_transmission_suspended ()) {
2156 framepos_t const f = _editor->playhead_cursor->current_frame;
2157 s->send_mmc_locate (f);
2158 s->send_full_time_code (f);
2161 show_verbose_cursor_time (t);
2162 _editor->UpdateAllTransportClocks (t);
2166 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2168 Drag::start_grab (event, c);
2170 _grab_zoom = _editor->frames_per_unit;
2172 framepos_t where = _editor->event_frame (event, 0, 0);
2173 _editor->snap_to_with_modifier (where, event);
2175 _editor->_dragging_playhead = true;
2177 Session* s = _editor->session ();
2180 if (_was_rolling && _stop) {
2184 if (s->is_auditioning()) {
2185 s->cancel_audition ();
2188 s->request_suspend_timecode_transmission ();
2189 while (!s->timecode_transmission_suspended ()) {
2190 /* twiddle our thumbs */
2194 fake_locate (where);
2198 CursorDrag::motion (GdkEvent* event, bool)
2200 framepos_t const adjusted_frame = adjusted_current_frame (event);
2201 if (adjusted_frame != last_pointer_frame()) {
2202 fake_locate (adjusted_frame);
2204 _editor->update_canvas_now ();
2210 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2212 _editor->_dragging_playhead = false;
2214 if (!movement_occurred && _stop) {
2218 motion (event, false);
2220 Session* s = _editor->session ();
2222 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2223 _editor->_pending_locate_request = true;
2224 s->request_resume_timecode_transmission ();
2229 CursorDrag::aborted (bool)
2231 if (_editor->_dragging_playhead) {
2232 _editor->session()->request_resume_timecode_transmission ();
2233 _editor->_dragging_playhead = false;
2236 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2239 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2240 : RegionDrag (e, i, p, v)
2242 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2246 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2248 Drag::start_grab (event, cursor);
2250 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2251 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2253 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2255 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2259 FadeInDrag::setup_pointer_frame_offset ()
2261 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2262 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2263 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2267 FadeInDrag::motion (GdkEvent* event, bool)
2269 framecnt_t fade_length;
2271 framepos_t const pos = adjusted_current_frame (event);
2273 boost::shared_ptr<Region> region = _primary->region ();
2275 if (pos < (region->position() + 64)) {
2276 fade_length = 64; // this should be a minimum defined somewhere
2277 } else if (pos > region->last_frame()) {
2278 fade_length = region->length();
2280 fade_length = pos - region->position();
2283 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2285 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2291 tmp->reset_fade_in_shape_width (fade_length);
2292 tmp->show_fade_line((framecnt_t) fade_length);
2295 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2299 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2301 if (!movement_occurred) {
2305 framecnt_t fade_length;
2307 framepos_t const pos = adjusted_current_frame (event);
2309 boost::shared_ptr<Region> region = _primary->region ();
2311 if (pos < (region->position() + 64)) {
2312 fade_length = 64; // this should be a minimum defined somewhere
2313 } else if (pos > region->last_frame()) {
2314 fade_length = region->length();
2316 fade_length = pos - region->position();
2319 _editor->begin_reversible_command (_("change fade in length"));
2321 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2323 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2329 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2330 XMLNode &before = alist->get_state();
2332 tmp->audio_region()->set_fade_in_length (fade_length);
2333 tmp->audio_region()->set_fade_in_active (true);
2334 tmp->hide_fade_line();
2336 XMLNode &after = alist->get_state();
2337 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2340 _editor->commit_reversible_command ();
2344 FadeInDrag::aborted (bool)
2346 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2347 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2353 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2354 tmp->hide_fade_line();
2358 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2359 : RegionDrag (e, i, p, v)
2361 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2365 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2367 Drag::start_grab (event, cursor);
2369 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2370 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2372 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2374 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2378 FadeOutDrag::setup_pointer_frame_offset ()
2380 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2381 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2382 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2386 FadeOutDrag::motion (GdkEvent* event, bool)
2388 framecnt_t fade_length;
2390 framepos_t const pos = adjusted_current_frame (event);
2392 boost::shared_ptr<Region> region = _primary->region ();
2394 if (pos > (region->last_frame() - 64)) {
2395 fade_length = 64; // this should really be a minimum fade defined somewhere
2397 else if (pos < region->position()) {
2398 fade_length = region->length();
2401 fade_length = region->last_frame() - pos;
2404 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2406 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2412 tmp->reset_fade_out_shape_width (fade_length);
2413 tmp->show_fade_line(region->length() - fade_length);
2416 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2420 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2422 if (!movement_occurred) {
2426 framecnt_t fade_length;
2428 framepos_t const pos = adjusted_current_frame (event);
2430 boost::shared_ptr<Region> region = _primary->region ();
2432 if (pos > (region->last_frame() - 64)) {
2433 fade_length = 64; // this should really be a minimum fade defined somewhere
2435 else if (pos < region->position()) {
2436 fade_length = region->length();
2439 fade_length = region->last_frame() - pos;
2442 _editor->begin_reversible_command (_("change fade out length"));
2444 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2446 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2452 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2453 XMLNode &before = alist->get_state();
2455 tmp->audio_region()->set_fade_out_length (fade_length);
2456 tmp->audio_region()->set_fade_out_active (true);
2457 tmp->hide_fade_line();
2459 XMLNode &after = alist->get_state();
2460 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2463 _editor->commit_reversible_command ();
2467 FadeOutDrag::aborted (bool)
2469 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2470 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2476 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2477 tmp->hide_fade_line();
2481 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2484 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2486 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2489 _points.push_back (Gnome::Art::Point (0, 0));
2490 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2493 MarkerDrag::~MarkerDrag ()
2495 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2501 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2503 Drag::start_grab (event, cursor);
2507 Location *location = _editor->find_location_from_marker (_marker, is_start);
2508 _editor->_dragging_edit_point = true;
2510 update_item (location);
2512 // _drag_line->show();
2513 // _line->raise_to_top();
2516 show_verbose_cursor_time (location->start());
2518 show_verbose_cursor_time (location->end());
2521 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2524 case Selection::Toggle:
2525 _editor->selection->toggle (_marker);
2527 case Selection::Set:
2528 if (!_editor->selection->selected (_marker)) {
2529 _editor->selection->set (_marker);
2532 case Selection::Extend:
2534 Locations::LocationList ll;
2535 list<Marker*> to_add;
2537 _editor->selection->markers.range (s, e);
2538 s = min (_marker->position(), s);
2539 e = max (_marker->position(), e);
2542 if (e < max_framepos) {
2545 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2546 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2547 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2550 to_add.push_back (lm->start);
2553 to_add.push_back (lm->end);
2557 if (!to_add.empty()) {
2558 _editor->selection->add (to_add);
2562 case Selection::Add:
2563 _editor->selection->add (_marker);
2567 /* Set up copies for us to manipulate during the drag */
2569 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2570 Location* l = _editor->find_location_from_marker (*i, is_start);
2571 _copied_locations.push_back (new Location (*l));
2576 MarkerDrag::setup_pointer_frame_offset ()
2579 Location *location = _editor->find_location_from_marker (_marker, is_start);
2580 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2584 MarkerDrag::motion (GdkEvent* event, bool)
2586 framecnt_t f_delta = 0;
2588 bool move_both = false;
2590 Location *real_location;
2591 Location *copy_location = 0;
2593 framepos_t const newframe = adjusted_current_frame (event);
2595 framepos_t next = newframe;
2597 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2601 MarkerSelection::iterator i;
2602 list<Location*>::iterator x;
2604 /* find the marker we're dragging, and compute the delta */
2606 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2607 x != _copied_locations.end() && i != _editor->selection->markers.end();
2613 if (marker == _marker) {
2615 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2620 if (real_location->is_mark()) {
2621 f_delta = newframe - copy_location->start();
2625 switch (marker->type()) {
2626 case Marker::SessionStart:
2627 case Marker::RangeStart:
2628 case Marker::LoopStart:
2629 case Marker::PunchIn:
2630 f_delta = newframe - copy_location->start();
2633 case Marker::SessionEnd:
2634 case Marker::RangeEnd:
2635 case Marker::LoopEnd:
2636 case Marker::PunchOut:
2637 f_delta = newframe - copy_location->end();
2640 /* what kind of marker is this ? */
2648 if (i == _editor->selection->markers.end()) {
2649 /* hmm, impossible - we didn't find the dragged marker */
2653 /* now move them all */
2655 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2656 x != _copied_locations.end() && i != _editor->selection->markers.end();
2662 /* call this to find out if its the start or end */
2664 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2668 if (real_location->locked()) {
2672 if (copy_location->is_mark()) {
2676 copy_location->set_start (copy_location->start() + f_delta);
2680 framepos_t new_start = copy_location->start() + f_delta;
2681 framepos_t new_end = copy_location->end() + f_delta;
2683 if (is_start) { // start-of-range marker
2686 copy_location->set_start (new_start);
2687 copy_location->set_end (new_end);
2688 } else if (new_start < copy_location->end()) {
2689 copy_location->set_start (new_start);
2690 } else if (newframe > 0) {
2691 _editor->snap_to (next, 1, true);
2692 copy_location->set_end (next);
2693 copy_location->set_start (newframe);
2696 } else { // end marker
2699 copy_location->set_end (new_end);
2700 copy_location->set_start (new_start);
2701 } else if (new_end > copy_location->start()) {
2702 copy_location->set_end (new_end);
2703 } else if (newframe > 0) {
2704 _editor->snap_to (next, -1, true);
2705 copy_location->set_start (next);
2706 copy_location->set_end (newframe);
2711 update_item (copy_location);
2713 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2716 lm->set_position (copy_location->start(), copy_location->end());
2720 assert (!_copied_locations.empty());
2722 show_verbose_cursor_time (newframe);
2725 _editor->update_canvas_now ();
2730 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2732 if (!movement_occurred) {
2734 /* just a click, do nothing but finish
2735 off the selection process
2738 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2741 case Selection::Set:
2742 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2743 _editor->selection->set (_marker);
2747 case Selection::Toggle:
2748 case Selection::Extend:
2749 case Selection::Add:
2756 _editor->_dragging_edit_point = false;
2758 _editor->begin_reversible_command ( _("move marker") );
2759 XMLNode &before = _editor->session()->locations()->get_state();
2761 MarkerSelection::iterator i;
2762 list<Location*>::iterator x;
2765 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2766 x != _copied_locations.end() && i != _editor->selection->markers.end();
2769 Location * location = _editor->find_location_from_marker (*i, is_start);
2773 if (location->locked()) {
2777 if (location->is_mark()) {
2778 location->set_start ((*x)->start());
2780 location->set ((*x)->start(), (*x)->end());
2785 XMLNode &after = _editor->session()->locations()->get_state();
2786 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2787 _editor->commit_reversible_command ();
2791 MarkerDrag::aborted (bool)
2797 MarkerDrag::update_item (Location*)
2802 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2804 _cumulative_x_drag (0),
2805 _cumulative_y_drag (0)
2807 if (_zero_gain_fraction < 0.0) {
2808 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2811 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2813 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2819 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2821 Drag::start_grab (event, _editor->cursors()->fader);
2823 // start the grab at the center of the control point so
2824 // the point doesn't 'jump' to the mouse after the first drag
2825 _fixed_grab_x = _point->get_x();
2826 _fixed_grab_y = _point->get_y();
2828 float const fraction = 1 - (_point->get_y() / _point->line().height());
2830 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2832 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2833 event->button.x + 10, event->button.y + 10);
2835 _editor->verbose_cursor()->show ();
2839 ControlPointDrag::motion (GdkEvent* event, bool)
2841 double dx = _drags->current_pointer_x() - last_pointer_x();
2842 double dy = _drags->current_pointer_y() - last_pointer_y();
2844 if (event->button.state & Keyboard::SecondaryModifier) {
2849 /* coordinate in pixels relative to the start of the region (for region-based automation)
2850 or track (for track-based automation) */
2851 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2852 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2854 // calculate zero crossing point. back off by .01 to stay on the
2855 // positive side of zero
2856 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2858 // make sure we hit zero when passing through
2859 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2863 if (_x_constrained) {
2866 if (_y_constrained) {
2870 _cumulative_x_drag = cx - _fixed_grab_x;
2871 _cumulative_y_drag = cy - _fixed_grab_y;
2875 cy = min ((double) _point->line().height(), cy);
2877 framepos_t cx_frames = _editor->unit_to_frame (cx);
2879 if (!_x_constrained) {
2880 _editor->snap_to_with_modifier (cx_frames, event);
2883 cx_frames = min (cx_frames, _point->line().maximum_time());
2885 float const fraction = 1.0 - (cy / _point->line().height());
2887 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2889 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2891 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2895 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2897 if (!movement_occurred) {
2901 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2902 _editor->reset_point_selection ();
2906 motion (event, false);
2909 _point->line().end_drag ();
2910 _editor->session()->commit_reversible_command ();
2914 ControlPointDrag::aborted (bool)
2916 _point->line().reset ();
2920 ControlPointDrag::active (Editing::MouseMode m)
2922 if (m == Editing::MouseGain) {
2923 /* always active in mouse gain */
2927 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2928 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2931 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2934 _cumulative_y_drag (0)
2936 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2940 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2942 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2945 _item = &_line->grab_item ();
2947 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2948 origin, and ditto for y.
2951 double cx = event->button.x;
2952 double cy = event->button.y;
2954 _line->parent_group().w2i (cx, cy);
2956 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2961 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2962 /* no adjacent points */
2966 Drag::start_grab (event, _editor->cursors()->fader);
2968 /* store grab start in parent frame */
2973 double fraction = 1.0 - (cy / _line->height());
2975 _line->start_drag_line (before, after, fraction);
2977 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2978 event->button.x + 10, event->button.y + 10);
2980 _editor->verbose_cursor()->show ();
2984 LineDrag::motion (GdkEvent* event, bool)
2986 double dy = _drags->current_pointer_y() - last_pointer_y();
2988 if (event->button.state & Keyboard::SecondaryModifier) {
2992 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2994 _cumulative_y_drag = cy - _fixed_grab_y;
2997 cy = min ((double) _line->height(), cy);
2999 double const fraction = 1.0 - (cy / _line->height());
3003 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3009 /* we are ignoring x position for this drag, so we can just pass in anything */
3010 _line->drag_motion (0, fraction, true, push);
3012 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3016 LineDrag::finished (GdkEvent* event, bool)
3018 motion (event, false);
3020 _editor->session()->commit_reversible_command ();
3024 LineDrag::aborted (bool)
3029 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3032 _cumulative_x_drag (0)
3034 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3038 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3040 Drag::start_grab (event);
3042 _line = reinterpret_cast<Line*> (_item);
3045 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3047 double cx = event->button.x;
3048 double cy = event->button.y;
3050 _item->property_parent().get_value()->w2i(cx, cy);
3052 /* store grab start in parent frame */
3053 _region_view_grab_x = cx;
3055 _before = *(float*) _item->get_data ("position");
3057 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3059 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3063 FeatureLineDrag::motion (GdkEvent*, bool)
3065 double dx = _drags->current_pointer_x() - last_pointer_x();
3067 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3069 _cumulative_x_drag += dx;
3071 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3080 ArdourCanvas::Points points;
3082 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3084 _line->get_bounds(x1, y2, x2, y2);
3086 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3087 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3089 _line->property_points() = points;
3091 float *pos = new float;
3094 _line->set_data ("position", pos);
3100 FeatureLineDrag::finished (GdkEvent*, bool)
3102 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3103 _arv->update_transient(_before, _before);
3107 FeatureLineDrag::aborted (bool)
3112 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3115 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3119 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3121 Drag::start_grab (event);
3122 show_verbose_cursor_time (adjusted_current_frame (event));
3126 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3133 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3135 framepos_t grab = grab_frame ();
3136 if (Config->get_rubberbanding_snaps_to_grid ()) {
3137 _editor->snap_to_with_modifier (grab, event);
3140 /* base start and end on initial click position */
3150 if (_drags->current_pointer_y() < grab_y()) {
3151 y1 = _drags->current_pointer_y();
3154 y2 = _drags->current_pointer_y();
3159 if (start != end || y1 != y2) {
3161 double x1 = _editor->frame_to_pixel (start);
3162 double x2 = _editor->frame_to_pixel (end);
3164 _editor->rubberband_rect->property_x1() = x1;
3165 _editor->rubberband_rect->property_y1() = y1;
3166 _editor->rubberband_rect->property_x2() = x2;
3167 _editor->rubberband_rect->property_y2() = y2;
3169 _editor->rubberband_rect->show();
3170 _editor->rubberband_rect->raise_to_top();
3172 show_verbose_cursor_time (pf);
3174 do_select_things (event, true);
3179 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3184 if (grab_frame() < last_pointer_frame()) {
3186 x2 = last_pointer_frame ();
3189 x1 = last_pointer_frame ();
3195 if (_drags->current_pointer_y() < grab_y()) {
3196 y1 = _drags->current_pointer_y();
3199 y2 = _drags->current_pointer_y();
3203 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3207 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3209 if (movement_occurred) {
3211 motion (event, false);
3212 do_select_things (event, false);
3220 _editor->rubberband_rect->hide();
3224 RubberbandSelectDrag::aborted (bool)
3226 _editor->rubberband_rect->hide ();
3229 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3230 : RegionDrag (e, i, p, v)
3232 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3236 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3238 Drag::start_grab (event, cursor);
3240 show_verbose_cursor_time (adjusted_current_frame (event));
3244 TimeFXDrag::motion (GdkEvent* event, bool)
3246 RegionView* rv = _primary;
3248 framepos_t const pf = adjusted_current_frame (event);
3250 if (pf > rv->region()->position()) {
3251 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3254 show_verbose_cursor_time (pf);
3258 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3260 _primary->get_time_axis_view().hide_timestretch ();
3262 if (!movement_occurred) {
3266 if (last_pointer_frame() < _primary->region()->position()) {
3267 /* backwards drag of the left edge - not usable */
3271 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3273 float percentage = (double) newlen / (double) _primary->region()->length();
3275 #ifndef USE_RUBBERBAND
3276 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3277 if (_primary->region()->data_type() == DataType::AUDIO) {
3278 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3282 // XXX how do timeFX on multiple regions ?
3287 if (_editor->time_stretch (rs, percentage) == -1) {
3288 error << _("An error occurred while executing time stretch operation") << endmsg;
3293 TimeFXDrag::aborted (bool)
3295 _primary->get_time_axis_view().hide_timestretch ();
3298 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3301 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3305 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3307 Drag::start_grab (event);
3311 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3313 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3317 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3319 if (movement_occurred && _editor->session()) {
3320 /* make sure we stop */
3321 _editor->session()->request_transport_speed (0.0);
3326 ScrubDrag::aborted (bool)
3331 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3335 , _original_pointer_time_axis (-1)
3336 , _last_pointer_time_axis (-1)
3338 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3342 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3344 if (_editor->session() == 0) {
3348 Gdk::Cursor* cursor = 0;
3350 switch (_operation) {
3351 case CreateSelection:
3352 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3357 cursor = _editor->cursors()->selector;
3358 Drag::start_grab (event, cursor);
3361 case SelectionStartTrim:
3362 if (_editor->clicked_axisview) {
3363 _editor->clicked_axisview->order_selection_trims (_item, true);
3365 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3368 case SelectionEndTrim:
3369 if (_editor->clicked_axisview) {
3370 _editor->clicked_axisview->order_selection_trims (_item, false);
3372 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3376 Drag::start_grab (event, cursor);
3380 if (_operation == SelectionMove) {
3381 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3383 show_verbose_cursor_time (adjusted_current_frame (event));
3386 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3390 SelectionDrag::setup_pointer_frame_offset ()
3392 switch (_operation) {
3393 case CreateSelection:
3394 _pointer_frame_offset = 0;
3397 case SelectionStartTrim:
3399 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3402 case SelectionEndTrim:
3403 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3409 SelectionDrag::motion (GdkEvent* event, bool first_move)
3411 framepos_t start = 0;
3415 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3416 if (pending_time_axis.first == 0) {
3420 framepos_t const pending_position = adjusted_current_frame (event);
3422 /* only alter selection if things have changed */
3424 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3428 switch (_operation) {
3429 case CreateSelection:
3431 framepos_t grab = grab_frame ();
3434 _editor->snap_to (grab);
3437 if (pending_position < grab_frame()) {
3438 start = pending_position;
3441 end = pending_position;
3445 /* first drag: Either add to the selection
3446 or create a new selection
3452 /* adding to the selection */
3453 _editor->set_selected_track_as_side_effect (Selection::Add);
3454 //_editor->selection->add (_editor->clicked_axisview);
3455 _editor->clicked_selection = _editor->selection->add (start, end);
3460 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3461 //_editor->selection->set (_editor->clicked_axisview);
3462 _editor->set_selected_track_as_side_effect (Selection::Set);
3465 _editor->clicked_selection = _editor->selection->set (start, end);
3469 /* select the track that we're in */
3470 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3471 // _editor->set_selected_track_as_side_effect (Selection::Add);
3472 _editor->selection->add (pending_time_axis.first);
3473 _added_time_axes.push_back (pending_time_axis.first);
3476 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3477 tracks that we selected in the first place.
3480 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3481 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3483 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3484 while (i != _added_time_axes.end()) {
3486 list<TimeAxisView*>::iterator tmp = i;
3489 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3490 _editor->selection->remove (*i);
3491 _added_time_axes.remove (*i);
3500 case SelectionStartTrim:
3502 start = _editor->selection->time[_editor->clicked_selection].start;
3503 end = _editor->selection->time[_editor->clicked_selection].end;
3505 if (pending_position > end) {
3508 start = pending_position;
3512 case SelectionEndTrim:
3514 start = _editor->selection->time[_editor->clicked_selection].start;
3515 end = _editor->selection->time[_editor->clicked_selection].end;
3517 if (pending_position < start) {
3520 end = pending_position;
3527 start = _editor->selection->time[_editor->clicked_selection].start;
3528 end = _editor->selection->time[_editor->clicked_selection].end;
3530 length = end - start;
3532 start = pending_position;
3533 _editor->snap_to (start);
3535 end = start + length;
3540 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3541 _editor->start_canvas_autoscroll (1, 0);
3545 _editor->selection->replace (_editor->clicked_selection, start, end);
3548 if (_operation == SelectionMove) {
3549 show_verbose_cursor_time(start);
3551 show_verbose_cursor_time(pending_position);
3556 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3558 Session* s = _editor->session();
3560 if (movement_occurred) {
3561 motion (event, false);
3562 /* XXX this is not object-oriented programming at all. ick */
3563 if (_editor->selection->time.consolidate()) {
3564 _editor->selection->TimeChanged ();
3567 /* XXX what if its a music time selection? */
3568 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3569 s->request_play_range (&_editor->selection->time, true);
3574 /* just a click, no pointer movement.*/
3576 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3577 _editor->selection->clear_time();
3580 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3581 _editor->selection->set (_editor->clicked_axisview);
3584 if (s && s->get_play_range () && s->transport_rolling()) {
3585 s->request_stop (false, false);
3590 _editor->stop_canvas_autoscroll ();
3594 SelectionDrag::aborted (bool)
3599 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3604 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3606 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3607 physical_screen_height (_editor->get_window()));
3608 _drag_rect->hide ();
3610 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3611 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3615 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3617 if (_editor->session() == 0) {
3621 Gdk::Cursor* cursor = 0;
3623 if (!_editor->temp_location) {
3624 _editor->temp_location = new Location (*_editor->session());
3627 switch (_operation) {
3628 case CreateRangeMarker:
3629 case CreateTransportMarker:
3630 case CreateCDMarker:
3632 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3637 cursor = _editor->cursors()->selector;
3641 Drag::start_grab (event, cursor);
3643 show_verbose_cursor_time (adjusted_current_frame (event));
3647 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3649 framepos_t start = 0;
3651 ArdourCanvas::SimpleRect *crect;
3653 switch (_operation) {
3654 case CreateRangeMarker:
3655 crect = _editor->range_bar_drag_rect;
3657 case CreateTransportMarker:
3658 crect = _editor->transport_bar_drag_rect;
3660 case CreateCDMarker:
3661 crect = _editor->cd_marker_bar_drag_rect;
3664 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3669 framepos_t const pf = adjusted_current_frame (event);
3671 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3672 framepos_t grab = grab_frame ();
3673 _editor->snap_to (grab);
3675 if (pf < grab_frame()) {
3683 /* first drag: Either add to the selection
3684 or create a new selection.
3689 _editor->temp_location->set (start, end);
3693 update_item (_editor->temp_location);
3695 //_drag_rect->raise_to_top();
3700 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3701 _editor->start_canvas_autoscroll (1, 0);
3705 _editor->temp_location->set (start, end);
3707 double x1 = _editor->frame_to_pixel (start);
3708 double x2 = _editor->frame_to_pixel (end);
3709 crect->property_x1() = x1;
3710 crect->property_x2() = x2;
3712 update_item (_editor->temp_location);
3715 show_verbose_cursor_time (pf);
3720 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3722 Location * newloc = 0;
3726 if (movement_occurred) {
3727 motion (event, false);
3730 switch (_operation) {
3731 case CreateRangeMarker:
3732 case CreateCDMarker:
3734 _editor->begin_reversible_command (_("new range marker"));
3735 XMLNode &before = _editor->session()->locations()->get_state();
3736 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3737 if (_operation == CreateCDMarker) {
3738 flags = Location::IsRangeMarker | Location::IsCDMarker;
3739 _editor->cd_marker_bar_drag_rect->hide();
3742 flags = Location::IsRangeMarker;
3743 _editor->range_bar_drag_rect->hide();
3745 newloc = new Location (
3746 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3749 _editor->session()->locations()->add (newloc, true);
3750 XMLNode &after = _editor->session()->locations()->get_state();
3751 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3752 _editor->commit_reversible_command ();
3756 case CreateTransportMarker:
3757 // popup menu to pick loop or punch
3758 _editor->new_transport_marker_context_menu (&event->button, _item);
3762 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3764 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3769 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3771 if (end == max_framepos) {
3772 end = _editor->session()->current_end_frame ();
3775 if (start == max_framepos) {
3776 start = _editor->session()->current_start_frame ();
3779 switch (_editor->mouse_mode) {
3781 /* find the two markers on either side and then make the selection from it */
3782 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3786 /* find the two markers on either side of the click and make the range out of it */
3787 _editor->selection->set (start, end);
3796 _editor->stop_canvas_autoscroll ();
3800 RangeMarkerBarDrag::aborted (bool)
3806 RangeMarkerBarDrag::update_item (Location* location)
3808 double const x1 = _editor->frame_to_pixel (location->start());
3809 double const x2 = _editor->frame_to_pixel (location->end());
3811 _drag_rect->property_x1() = x1;
3812 _drag_rect->property_x2() = x2;
3815 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3819 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3823 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3825 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3826 Drag::start_grab (event, _editor->cursors()->zoom_out);
3829 Drag::start_grab (event, _editor->cursors()->zoom_in);
3833 show_verbose_cursor_time (adjusted_current_frame (event));
3837 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3842 framepos_t const pf = adjusted_current_frame (event);
3844 framepos_t grab = grab_frame ();
3845 _editor->snap_to_with_modifier (grab, event);
3847 /* base start and end on initial click position */
3859 _editor->zoom_rect->show();
3860 _editor->zoom_rect->raise_to_top();
3863 _editor->reposition_zoom_rect(start, end);
3865 show_verbose_cursor_time (pf);
3870 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3872 if (movement_occurred) {
3873 motion (event, false);
3875 if (grab_frame() < last_pointer_frame()) {
3876 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3878 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3881 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3882 _editor->tav_zoom_step (_zoom_out);
3884 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3888 _editor->zoom_rect->hide();
3892 MouseZoomDrag::aborted (bool)
3894 _editor->zoom_rect->hide ();
3897 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3899 , _cumulative_dx (0)
3900 , _cumulative_dy (0)
3902 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3904 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3905 _region = &_primary->region_view ();
3906 _note_height = _region->midi_stream_view()->note_height ();
3910 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3912 Drag::start_grab (event);
3914 if (!(_was_selected = _primary->selected())) {
3916 /* tertiary-click means extend selection - we'll do that on button release,
3917 so don't add it here, because otherwise we make it hard to figure
3918 out the "extend-to" range.
3921 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3924 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3927 _region->note_selected (_primary, true);
3929 _region->unique_select (_primary);
3935 /** @return Current total drag x change in frames */
3937 NoteDrag::total_dx () const
3940 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3942 /* primary note time */
3943 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3945 /* new time of the primary note in session frames */
3946 frameoffset_t st = n + dx;
3948 framepos_t const rp = _region->region()->position ();
3950 /* prevent the note being dragged earlier than the region's position */
3953 /* snap and return corresponding delta */
3954 return _region->snap_frame_to_frame (st - rp) + rp - n;
3957 /** @return Current total drag y change in note number */
3959 NoteDrag::total_dy () const
3961 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3965 NoteDrag::motion (GdkEvent *, bool)
3967 /* Total change in x and y since the start of the drag */
3968 frameoffset_t const dx = total_dx ();
3969 int8_t const dy = total_dy ();
3971 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3972 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3973 double const tdy = -dy * _note_height - _cumulative_dy;
3976 _cumulative_dx += tdx;
3977 _cumulative_dy += tdy;
3979 int8_t note_delta = total_dy();
3981 _region->move_selection (tdx, tdy, note_delta);
3983 /* the new note value may be the same as the old one, but we
3984 * don't know what that means because the selection may have
3985 * involved more than one note and we might be doing something
3986 * odd with them. so show the note value anyway, always.
3990 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3992 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3993 (int) floor (new_note));
3995 show_verbose_cursor_text (buf);
4000 NoteDrag::finished (GdkEvent* ev, bool moved)
4003 if (_editor->current_mouse_mode() == Editing::MouseObject) {
4005 if (_was_selected) {
4006 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4008 _region->note_deselected (_primary);
4011 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4012 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4014 if (!extend && !add && _region->selection_size() > 1) {
4015 _region->unique_select (_primary);
4016 } else if (extend) {
4017 _region->note_selected (_primary, true, true);
4019 /* it was added during button press */
4024 _region->note_dropped (_primary, total_dx(), total_dy());
4029 NoteDrag::aborted (bool)
4034 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
4035 : Drag (editor, item)
4037 , _nothing_to_drag (false)
4039 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4041 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
4044 /* get all lines in the automation view */
4045 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
4047 /* find those that overlap the ranges being dragged */
4048 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
4049 while (i != lines.end ()) {
4050 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4053 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4055 /* check this range against all the AudioRanges that we are using */
4056 list<AudioRange>::const_iterator k = _ranges.begin ();
4057 while (k != _ranges.end()) {
4058 if (k->coverage (r.first, r.second) != OverlapNone) {
4064 /* add it to our list if it overlaps at all */
4065 if (k != _ranges.end()) {
4070 _lines.push_back (n);
4076 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4080 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4082 Drag::start_grab (event, cursor);
4084 /* Get line states before we start changing things */
4085 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4086 i->state = &i->line->get_state ();
4089 if (_ranges.empty()) {
4091 /* No selected time ranges: drag all points */
4092 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4093 uint32_t const N = i->line->npoints ();
4094 for (uint32_t j = 0; j < N; ++j) {
4095 i->points.push_back (i->line->nth (j));
4101 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4103 framecnt_t const half = (i->start + i->end) / 2;
4105 /* find the line that this audio range starts in */
4106 list<Line>::iterator j = _lines.begin();
4107 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4111 if (j != _lines.end()) {
4112 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4114 /* j is the line that this audio range starts in; fade into it;
4115 64 samples length plucked out of thin air.
4118 framepos_t a = i->start + 64;
4123 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4124 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4126 the_list->add (p, the_list->eval (p));
4127 j->line->add_always_in_view (p);
4128 the_list->add (q, the_list->eval (q));
4129 j->line->add_always_in_view (q);
4132 /* same thing for the end */
4135 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4139 if (j != _lines.end()) {
4140 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4142 /* j is the line that this audio range starts in; fade out of it;
4143 64 samples length plucked out of thin air.
4146 framepos_t b = i->end - 64;
4151 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4152 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4154 the_list->add (p, the_list->eval (p));
4155 j->line->add_always_in_view (p);
4156 the_list->add (q, the_list->eval (q));
4157 j->line->add_always_in_view (q);
4161 _nothing_to_drag = true;
4163 /* Find all the points that should be dragged and put them in the relevant
4164 points lists in the Line structs.
4167 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4169 uint32_t const N = i->line->npoints ();
4170 for (uint32_t j = 0; j < N; ++j) {
4172 /* here's a control point on this line */
4173 ControlPoint* p = i->line->nth (j);
4174 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4176 /* see if it's inside a range */
4177 list<AudioRange>::const_iterator k = _ranges.begin ();
4178 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4182 if (k != _ranges.end()) {
4183 /* dragging this point */
4184 _nothing_to_drag = false;
4185 i->points.push_back (p);
4191 if (_nothing_to_drag) {
4195 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4196 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4201 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4203 if (_nothing_to_drag) {
4207 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4208 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4210 /* we are ignoring x position for this drag, so we can just pass in anything */
4211 i->line->drag_motion (0, f, true, false);
4216 AutomationRangeDrag::finished (GdkEvent* event, bool)
4218 if (_nothing_to_drag) {
4222 motion (event, false);
4223 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4224 i->line->end_drag ();
4225 i->line->clear_always_in_view ();
4228 _editor->session()->commit_reversible_command ();
4232 AutomationRangeDrag::aborted (bool)
4234 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4235 i->line->clear_always_in_view ();
4240 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4243 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4244 layer = v->region()->layer ();
4245 initial_y = v->get_canvas_group()->property_y ();
4246 initial_playlist = v->region()->playlist ();
4247 initial_position = v->region()->position ();
4248 initial_end = v->region()->position () + v->region()->length ();
4251 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4255 , _cumulative_dx (0)
4257 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4261 PatchChangeDrag::motion (GdkEvent* ev, bool)
4263 framepos_t f = adjusted_current_frame (ev);
4264 boost::shared_ptr<Region> r = _region_view->region ();
4265 f = max (f, r->position ());
4266 f = min (f, r->last_frame ());
4268 framecnt_t const dxf = f - grab_frame();
4269 double const dxu = _editor->frame_to_unit (dxf);
4270 _patch_change->move (dxu - _cumulative_dx, 0);
4271 _cumulative_dx = dxu;
4275 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4277 if (!movement_occurred) {
4281 boost::shared_ptr<Region> r (_region_view->region ());
4283 framepos_t f = adjusted_current_frame (ev);
4284 f = max (f, r->position ());
4285 f = min (f, r->last_frame ());
4287 _region_view->move_patch_change (
4289 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4294 PatchChangeDrag::aborted (bool)
4296 _patch_change->move (-_cumulative_dx, 0);
4300 PatchChangeDrag::setup_pointer_frame_offset ()
4302 boost::shared_ptr<Region> region = _region_view->region ();
4303 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4306 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4307 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4314 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4316 framepos_t const p = _region_view->region()->position ();
4317 double const y = _region_view->midi_view()->y_position ();
4319 x1 = max ((framepos_t) 0, x1 - p);
4320 x2 = max ((framepos_t) 0, x2 - p);
4321 y1 = max (0.0, y1 - y);
4322 y2 = max (0.0, y2 - y);
4324 _region_view->update_drag_selection (
4325 _editor->frame_to_pixel (x1),
4326 _editor->frame_to_pixel (x2),
4329 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4334 MidiRubberbandSelectDrag::deselect_things ()
4339 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4340 : RubberbandSelectDrag (e, i)
4346 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4348 if (drag_in_progress) {
4349 /* We just want to select things at the end of the drag, not during it */
4353 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4355 _editor->begin_reversible_command (_("rubberband selection"));
4356 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4357 _editor->commit_reversible_command ();
4361 EditorRubberbandSelectDrag::deselect_things ()
4363 if (!getenv("ARDOUR_SAE")) {
4364 _editor->selection->clear_tracks();
4366 _editor->selection->clear_regions();
4367 _editor->selection->clear_points ();
4368 _editor->selection->clear_lines ();
4371 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4379 NoteCreateDrag::~NoteCreateDrag ()
4385 NoteCreateDrag::grid_frames (framepos_t t) const
4388 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4393 return _region_view->region_beats_to_region_frames (grid_beats);
4397 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4399 Drag::start_grab (event, cursor);
4401 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4403 framepos_t pf = _drags->current_pointer_frame ();
4404 framecnt_t const g = grid_frames (pf);
4406 /* Hack so that we always snap to the note that we are over, instead of snapping
4407 to the next one if we're more than halfway through the one we're over.
4409 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4413 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4415 MidiStreamView* sv = _region_view->midi_stream_view ();
4416 double const x = _editor->frame_to_pixel (_note[0]);
4417 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4419 _drag_rect->property_x1() = x;
4420 _drag_rect->property_y1() = y;
4421 _drag_rect->property_x2() = x;
4422 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4424 _drag_rect->property_outline_what() = 0xff;
4425 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4426 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4430 NoteCreateDrag::motion (GdkEvent* event, bool)
4432 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4433 double const x = _editor->frame_to_pixel (_note[1]);
4434 if (_note[1] > _note[0]) {
4435 _drag_rect->property_x2() = x;
4437 _drag_rect->property_x1() = x;
4442 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4444 if (!had_movement) {
4448 framepos_t const start = min (_note[0], _note[1]);
4449 framecnt_t length = abs (_note[0] - _note[1]);
4451 framecnt_t const g = grid_frames (start);
4452 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4454 if (_editor->snap_mode() == SnapNormal && length < g) {
4455 length = g - one_tick;
4458 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4460 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4464 NoteCreateDrag::y_to_region (double y) const
4467 _region_view->get_canvas_group()->w2i (x, y);
4472 NoteCreateDrag::aborted (bool)