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 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time);
210 _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)
217 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
219 if (Keyboard::is_button2_event (&event->button)) {
220 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
221 _y_constrained = true;
222 _x_constrained = false;
224 _y_constrained = false;
225 _x_constrained = true;
228 _x_constrained = false;
229 _y_constrained = false;
232 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
233 setup_pointer_frame_offset ();
234 _grab_frame = adjusted_frame (_raw_grab_frame, event);
235 _last_pointer_frame = _grab_frame;
236 _last_pointer_x = _grab_x;
237 _last_pointer_y = _grab_y;
240 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
243 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
248 if (_editor->session() && _editor->session()->transport_rolling()) {
251 _was_rolling = false;
254 switch (_editor->snap_type()) {
255 case SnapToRegionStart:
256 case SnapToRegionEnd:
257 case SnapToRegionSync:
258 case SnapToRegionBoundary:
259 _editor->build_region_boundary_cache ();
266 /** Call to end a drag `successfully'. Ungrabs item and calls
267 * subclass' finished() method.
269 * @param event GDK event, or 0.
270 * @return true if some movement occurred, otherwise false.
273 Drag::end_grab (GdkEvent* event)
275 _editor->stop_canvas_autoscroll ();
277 _item->ungrab (event ? event->button.time : 0);
279 finished (event, _move_threshold_passed);
281 _editor->verbose_cursor()->hide ();
283 return _move_threshold_passed;
287 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
291 if (f > _pointer_frame_offset) {
292 pos = f - _pointer_frame_offset;
296 _editor->snap_to_with_modifier (pos, event);
303 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
305 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
309 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
311 /* check to see if we have moved in any way that matters since the last motion event */
312 if (_move_threshold_passed &&
313 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
314 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
318 pair<framecnt_t, int> const threshold = move_threshold ();
320 bool const old_move_threshold_passed = _move_threshold_passed;
322 if (!from_autoscroll && !_move_threshold_passed) {
324 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
325 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
327 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
330 if (active (_editor->mouse_mode) && _move_threshold_passed) {
332 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
333 if (!from_autoscroll) {
334 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
335 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
336 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
339 motion (event, _move_threshold_passed != old_move_threshold_passed);
341 _last_pointer_x = _drags->current_pointer_x ();
342 _last_pointer_y = _drags->current_pointer_y ();
343 _last_pointer_frame = adjusted_current_frame (event);
351 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
359 aborted (_move_threshold_passed);
361 _editor->stop_canvas_autoscroll ();
362 _editor->verbose_cursor()->hide ();
366 Drag::show_verbose_cursor_time (framepos_t frame)
368 _editor->verbose_cursor()->set_time (
370 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
371 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
374 _editor->verbose_cursor()->show ();
378 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
380 _editor->verbose_cursor()->show (xoffset);
382 _editor->verbose_cursor()->set_duration (
384 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
385 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
390 Drag::show_verbose_cursor_text (string const & text)
392 _editor->verbose_cursor()->show ();
394 _editor->verbose_cursor()->set (
396 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
397 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
401 boost::shared_ptr<Region>
402 Drag::add_midi_region (MidiTimeAxisView* view)
404 if (_editor->session()) {
405 const TempoMap& map (_editor->session()->tempo_map());
406 framecnt_t pos = grab_frame();
407 const Meter& m = map.meter_at (pos);
408 /* not that the frame rate used here can be affected by pull up/down which
411 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
412 return view->add_region (grab_frame(), len, true);
415 return boost::shared_ptr<Region>();
418 struct EditorOrderTimeAxisViewSorter {
419 bool operator() (TimeAxisView* a, TimeAxisView* b) {
420 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
421 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
423 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
427 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
431 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
433 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
434 as some of the regions we are dragging may be on such tracks.
437 TrackViewList track_views = _editor->track_views;
438 track_views.sort (EditorOrderTimeAxisViewSorter ());
440 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
441 _time_axis_views.push_back (*i);
443 TimeAxisView::Children children_list = (*i)->get_child_list ();
444 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
445 _time_axis_views.push_back (j->get());
449 /* the list of views can be empty at this point if this is a region list-insert drag
452 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
453 _views.push_back (DraggingView (*i, this));
456 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
460 RegionDrag::region_going_away (RegionView* v)
462 list<DraggingView>::iterator i = _views.begin ();
463 while (i != _views.end() && i->view != v) {
467 if (i != _views.end()) {
472 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
473 * or -1 if it is not found.
476 RegionDrag::find_time_axis_view (TimeAxisView* t) const
479 int const N = _time_axis_views.size ();
480 while (i < N && _time_axis_views[i] != t) {
491 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
492 : RegionDrag (e, i, p, v),
501 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
503 Drag::start_grab (event, cursor);
505 show_verbose_cursor_time (_last_frame_position);
507 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
508 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
509 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
513 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
515 /* compute the amount of pointer motion in frames, and where
516 the region would be if we moved it by that much.
518 *pending_region_position = adjusted_current_frame (event);
520 framepos_t sync_frame;
521 framecnt_t sync_offset;
524 sync_offset = _primary->region()->sync_offset (sync_dir);
526 /* we don't handle a sync point that lies before zero.
528 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
530 sync_frame = *pending_region_position + (sync_dir*sync_offset);
532 _editor->snap_to_with_modifier (sync_frame, event);
534 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
537 *pending_region_position = _last_frame_position;
540 if (*pending_region_position > max_framepos - _primary->region()->length()) {
541 *pending_region_position = _last_frame_position;
546 /* in locked edit mode, reverse the usual meaning of _x_constrained */
547 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
549 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
551 /* x movement since last time (in pixels) */
552 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
554 /* total x movement */
555 framecnt_t total_dx = *pending_region_position;
556 if (regions_came_from_canvas()) {
557 total_dx = total_dx - grab_frame ();
560 /* check that no regions have gone off the start of the session */
561 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
562 if ((i->view->region()->position() + total_dx) < 0) {
564 *pending_region_position = _last_frame_position;
569 _last_frame_position = *pending_region_position;
576 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
578 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
579 int const n = i->time_axis_view + delta_track;
580 if (n < 0 || n >= int (_time_axis_views.size())) {
581 /* off the top or bottom track */
585 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
586 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
587 /* not a track, or the wrong type */
591 double const l = i->layer + delta_layer;
593 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
594 mode to allow the user to place a region below another on layer 0.
596 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
597 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
598 If it has, the layers will be munged later anyway, so it's ok.
604 /* all regions being dragged are ok with this change */
609 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
611 assert (!_views.empty ());
613 /* Find the TimeAxisView that the pointer is now over */
614 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
616 if (first_move && tv.first->view()->layer_display() == Stacked) {
617 tv.first->view()->set_layer_display (Expanded);
620 /* Bail early if we're not over a track */
621 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
622 if (!rtv || !rtv->is_track()) {
623 _editor->verbose_cursor()->hide ();
627 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
629 /* Here's the current pointer position in terms of time axis view and layer */
630 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
631 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
633 /* Work out the change in x */
634 framepos_t pending_region_position;
635 double const x_delta = compute_x_delta (event, &pending_region_position);
637 /* Work out the change in y */
638 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
639 double delta_layer = current_pointer_layer - _last_pointer_layer;
641 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
642 /* this y movement is not allowed, so do no y movement this time */
643 delta_time_axis_view = 0;
647 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
648 /* haven't reached next snap point, and we're not switching
649 trackviews nor layers. nothing to do.
654 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
656 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
658 RegionView* rv = i->view;
660 if (rv->region()->locked()) {
666 rv->get_time_axis_view().hide_dependent_views (*rv);
668 /* Absolutely no idea why this is necessary, but it is; without
669 it, the region view disappears after the reparent.
671 _editor->update_canvas_now ();
673 /* Reparent to a non scrolling group so that we can keep the
674 region selection above all time axis views.
675 Reparenting means that we will have to move the region view
676 later, as the two parent groups have different coordinates.
679 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
681 rv->fake_set_opaque (true);
684 /* If we have moved tracks, we'll fudge the layer delta so that the
685 region gets moved back onto layer 0 on its new track; this avoids
686 confusion when dragging regions from non-zero layers onto different
689 double this_delta_layer = delta_layer;
690 if (delta_time_axis_view != 0) {
691 this_delta_layer = - i->layer;
694 /* The TimeAxisView that this region is now on */
695 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
697 /* Ensure it is moved from stacked -> expanded if appropriate */
698 if (tv->view()->layer_display() == Stacked) {
699 tv->view()->set_layer_display (Expanded);
702 /* We're only allowed to go -ve in layer on Expanded views */
703 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
704 this_delta_layer = - i->layer;
708 rv->set_height (tv->view()->child_height ());
710 /* Update show/hidden status as the region view may have come from a hidden track,
711 or have moved to one.
714 rv->get_canvas_group()->hide ();
716 rv->get_canvas_group()->show ();
719 /* Update the DraggingView */
720 i->time_axis_view += delta_time_axis_view;
721 i->layer += this_delta_layer;
724 _editor->mouse_brush_insert_region (rv, pending_region_position);
729 /* Get the y coordinate of the top of the track that this region is now on */
730 tv->canvas_display()->i2w (x, y);
731 y += _editor->get_trackview_group_vertical_offset();
733 /* And adjust for the layer that it should be on */
734 StreamView* cv = tv->view ();
735 switch (cv->layer_display ()) {
739 y += (cv->layers() - i->layer - 1) * cv->child_height ();
742 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
746 /* Now move the region view */
747 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
750 } /* foreach region */
752 _total_x_delta += x_delta;
755 _editor->cursor_group->raise_to_top();
758 if (x_delta != 0 && !_brushing) {
759 show_verbose_cursor_time (_last_frame_position);
762 _last_pointer_time_axis_view += delta_time_axis_view;
763 _last_pointer_layer += delta_layer;
767 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
769 if (_copy && first_move) {
771 /* duplicate the regionview(s) and region(s) */
773 list<DraggingView> new_regionviews;
775 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
777 RegionView* rv = i->view;
778 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
779 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
781 const boost::shared_ptr<const Region> original = rv->region();
782 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
783 region_copy->set_position (original->position());
787 boost::shared_ptr<AudioRegion> audioregion_copy
788 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
790 nrv = new AudioRegionView (*arv, audioregion_copy);
792 boost::shared_ptr<MidiRegion> midiregion_copy
793 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
794 nrv = new MidiRegionView (*mrv, midiregion_copy);
799 nrv->get_canvas_group()->show ();
800 new_regionviews.push_back (DraggingView (nrv, this));
802 /* swap _primary to the copy */
804 if (rv == _primary) {
808 /* ..and deselect the one we copied */
810 rv->set_selected (false);
813 if (!new_regionviews.empty()) {
815 /* reflect the fact that we are dragging the copies */
817 _views = new_regionviews;
819 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
822 sync the canvas to what we think is its current state
823 without it, the canvas seems to
824 "forget" to update properly after the upcoming reparent()
825 ..only if the mouse is in rapid motion at the time of the grab.
826 something to do with regionview creation taking so long?
828 _editor->update_canvas_now();
832 RegionMotionDrag::motion (event, first_move);
836 RegionMotionDrag::finished (GdkEvent *, bool)
838 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
843 if ((*i)->view()->layer_display() == Expanded) {
844 (*i)->view()->set_layer_display (Stacked);
850 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
852 RegionMotionDrag::finished (ev, movement_occurred);
854 if (!movement_occurred) {
859 /* reverse this here so that we have the correct logic to finalize
863 if (Config->get_edit_mode() == Lock) {
864 _x_constrained = !_x_constrained;
867 assert (!_views.empty ());
869 /* We might have hidden region views so that they weren't visible during the drag
870 (when they have been reparented). Now everything can be shown again, as region
871 views are back in their track parent groups.
873 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
874 i->view->get_canvas_group()->show ();
877 bool const changed_position = (_last_frame_position != _primary->region()->position());
878 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
879 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
881 _editor->update_canvas_now ();
903 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
905 RegionSelection new_views;
906 PlaylistSet modified_playlists;
907 list<RegionView*> views_to_delete;
910 /* all changes were made during motion event handlers */
912 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
916 _editor->commit_reversible_command ();
920 if (_x_constrained) {
921 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
923 _editor->begin_reversible_command (Operations::region_copy);
926 /* insert the regions into their new playlists */
927 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
929 if (i->view->region()->locked()) {
935 if (changed_position && !_x_constrained) {
936 where = i->view->region()->position() - drag_delta;
938 where = i->view->region()->position();
941 RegionView* new_view = insert_region_into_playlist (
942 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
949 new_views.push_back (new_view);
951 /* we don't need the copied RegionView any more */
952 views_to_delete.push_back (i->view);
955 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
956 because when views are deleted they are automagically removed from _views, which messes
959 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
963 /* If we've created new regions either by copying or moving
964 to a new track, we want to replace the old selection with the new ones
967 if (new_views.size() > 0) {
968 _editor->selection->set (new_views);
971 /* write commands for the accumulated diffs for all our modified playlists */
972 add_stateful_diff_commands_for_playlists (modified_playlists);
974 _editor->commit_reversible_command ();
978 RegionMoveDrag::finished_no_copy (
979 bool const changed_position,
980 bool const changed_tracks,
981 framecnt_t const drag_delta
984 RegionSelection new_views;
985 PlaylistSet modified_playlists;
986 PlaylistSet frozen_playlists;
987 set<RouteTimeAxisView*> views_to_update;
990 /* all changes were made during motion event handlers */
991 _editor->commit_reversible_command ();
995 if (_x_constrained) {
996 _editor->begin_reversible_command (_("fixed time region drag"));
998 _editor->begin_reversible_command (Operations::region_drag);
1001 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1003 RegionView* rv = i->view;
1005 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1006 double const dest_layer = i->layer;
1008 if (rv->region()->locked()) {
1013 views_to_update.insert (dest_rtv);
1017 if (changed_position && !_x_constrained) {
1018 where = rv->region()->position() - drag_delta;
1020 where = rv->region()->position();
1023 if (changed_tracks) {
1025 /* insert into new playlist */
1027 RegionView* new_view = insert_region_into_playlist (
1028 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1031 if (new_view == 0) {
1036 new_views.push_back (new_view);
1038 /* remove from old playlist */
1040 /* the region that used to be in the old playlist is not
1041 moved to the new one - we use a copy of it. as a result,
1042 any existing editor for the region should no longer be
1045 rv->hide_region_editor();
1046 rv->fake_set_opaque (false);
1048 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1052 rv->region()->clear_changes ();
1055 motion on the same track. plonk the previously reparented region
1056 back to its original canvas group (its streamview).
1057 No need to do anything for copies as they are fake regions which will be deleted.
1060 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1061 rv->get_canvas_group()->property_y() = i->initial_y;
1062 rv->get_time_axis_view().reveal_dependent_views (*rv);
1064 /* just change the model */
1066 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1068 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1069 playlist->set_layer (rv->region(), dest_layer);
1072 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1074 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1077 playlist->freeze ();
1080 /* this movement may result in a crossfade being modified, so we need to get undo
1081 data from the playlist as well as the region.
1084 r = modified_playlists.insert (playlist);
1086 playlist->clear_changes ();
1089 rv->region()->set_position (where);
1091 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1094 if (changed_tracks) {
1096 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1097 was selected in all of them, then removing it from a playlist will have removed all
1098 trace of it from _views (i.e. there were N regions selected, we removed 1,
1099 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1100 corresponding regionview, and _views is now empty).
1102 This could have invalidated any and all iterators into _views.
1104 The heuristic we use here is: if the region selection is empty, break out of the loop
1105 here. if the region selection is not empty, then restart the loop because we know that
1106 we must have removed at least the region(view) we've just been working on as well as any
1107 that we processed on previous iterations.
1109 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1110 we can just iterate.
1114 if (_views.empty()) {
1125 /* If we've created new regions either by copying or moving
1126 to a new track, we want to replace the old selection with the new ones
1129 if (new_views.size() > 0) {
1130 _editor->selection->set (new_views);
1133 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1137 /* write commands for the accumulated diffs for all our modified playlists */
1138 add_stateful_diff_commands_for_playlists (modified_playlists);
1140 _editor->commit_reversible_command ();
1142 /* We have futzed with the layering of canvas items on our streamviews.
1143 If any region changed layer, this will have resulted in the stream
1144 views being asked to set up their region views, and all will be well.
1145 If not, we might now have badly-ordered region views. Ask the StreamViews
1146 involved to sort themselves out, just in case.
1149 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1150 (*i)->view()->playlist_layered ((*i)->track ());
1154 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1155 * @param region Region to remove.
1156 * @param playlist playlist To remove from.
1157 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1158 * that clear_changes () is only called once per playlist.
1161 RegionMoveDrag::remove_region_from_playlist (
1162 boost::shared_ptr<Region> region,
1163 boost::shared_ptr<Playlist> playlist,
1164 PlaylistSet& modified_playlists
1167 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1170 playlist->clear_changes ();
1173 playlist->remove_region (region);
1177 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1178 * clearing the playlist's diff history first if necessary.
1179 * @param region Region to insert.
1180 * @param dest_rtv Destination RouteTimeAxisView.
1181 * @param dest_layer Destination layer.
1182 * @param where Destination position.
1183 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1184 * that clear_changes () is only called once per playlist.
1185 * @return New RegionView, or 0 if no insert was performed.
1188 RegionMoveDrag::insert_region_into_playlist (
1189 boost::shared_ptr<Region> region,
1190 RouteTimeAxisView* dest_rtv,
1193 PlaylistSet& modified_playlists
1196 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1197 if (!dest_playlist) {
1201 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1202 _new_region_view = 0;
1203 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1205 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1206 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1208 dest_playlist->clear_changes ();
1211 dest_playlist->add_region (region, where);
1213 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1214 dest_playlist->set_layer (region, dest_layer);
1219 assert (_new_region_view);
1221 return _new_region_view;
1225 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1227 _new_region_view = rv;
1231 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1233 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1234 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1236 _editor->session()->add_command (c);
1245 RegionMoveDrag::aborted (bool movement_occurred)
1249 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1256 RegionMotionDrag::aborted (movement_occurred);
1261 RegionMotionDrag::aborted (bool)
1263 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1264 if ((*i)->view()->layer_display() == Expanded) {
1265 (*i)->view()->set_layer_display (Stacked);
1269 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1270 RegionView* rv = i->view;
1271 TimeAxisView* tv = &(rv->get_time_axis_view ());
1272 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1274 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1275 rv->get_canvas_group()->property_y() = 0;
1276 rv->get_time_axis_view().reveal_dependent_views (*rv);
1277 rv->fake_set_opaque (false);
1278 rv->move (-_total_x_delta, 0);
1279 rv->set_height (rtv->view()->child_height ());
1282 _editor->update_canvas_now ();
1285 /** @param b true to brush, otherwise false.
1286 * @param c true to make copies of the regions being moved, otherwise false.
1288 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1289 : RegionMotionDrag (e, i, p, v, b),
1292 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1295 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1296 if (rtv && rtv->is_track()) {
1297 speed = rtv->track()->speed ();
1300 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1304 RegionMoveDrag::setup_pointer_frame_offset ()
1306 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1309 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1310 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1312 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1314 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1315 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1317 _primary = v->view()->create_region_view (r, false, false);
1319 _primary->get_canvas_group()->show ();
1320 _primary->set_position (pos, 0);
1321 _views.push_back (DraggingView (_primary, this));
1323 _last_frame_position = pos;
1325 _item = _primary->get_canvas_group ();
1329 RegionInsertDrag::finished (GdkEvent *, bool)
1331 _editor->update_canvas_now ();
1333 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1335 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1336 _primary->get_canvas_group()->property_y() = 0;
1338 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1340 _editor->begin_reversible_command (Operations::insert_region);
1341 playlist->clear_changes ();
1342 playlist->add_region (_primary->region (), _last_frame_position);
1343 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1344 _editor->commit_reversible_command ();
1352 RegionInsertDrag::aborted (bool)
1359 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1360 : RegionMoveDrag (e, i, p, v, false, false)
1362 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1365 struct RegionSelectionByPosition {
1366 bool operator() (RegionView*a, RegionView* b) {
1367 return a->region()->position () < b->region()->position();
1372 RegionSpliceDrag::motion (GdkEvent* event, bool)
1374 /* Which trackview is this ? */
1376 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1377 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1379 /* The region motion is only processed if the pointer is over
1383 if (!tv || !tv->is_track()) {
1384 /* To make sure we hide the verbose canvas cursor when the mouse is
1385 not held over and audiotrack.
1387 _editor->verbose_cursor()->hide ();
1393 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1399 RegionSelection copy (_editor->selection->regions);
1401 RegionSelectionByPosition cmp;
1404 framepos_t const pf = adjusted_current_frame (event);
1406 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1408 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1414 boost::shared_ptr<Playlist> playlist;
1416 if ((playlist = atv->playlist()) == 0) {
1420 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1425 if (pf < (*i)->region()->last_frame() + 1) {
1429 if (pf > (*i)->region()->first_frame()) {
1435 playlist->shuffle ((*i)->region(), dir);
1440 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1442 RegionMoveDrag::finished (event, movement_occurred);
1446 RegionSpliceDrag::aborted (bool)
1451 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1453 _view (dynamic_cast<MidiTimeAxisView*> (v))
1455 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1461 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1464 _region = add_midi_region (_view);
1465 _view->playlist()->freeze ();
1468 framepos_t const f = adjusted_current_frame (event);
1469 if (f < grab_frame()) {
1470 _region->set_position (f);
1473 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1474 so that if this region is duplicated, its duplicate starts on
1475 a snap point rather than 1 frame after a snap point. Otherwise things get
1476 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1477 place snapped notes at the start of the region.
1480 framecnt_t const len = abs (f - grab_frame () - 1);
1481 _region->set_length (len < 1 ? 1 : len);
1487 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1489 if (!movement_occurred) {
1490 add_midi_region (_view);
1492 _view->playlist()->thaw ();
1496 _editor->commit_reversible_command ();
1501 RegionCreateDrag::aborted (bool)
1504 _view->playlist()->thaw ();
1510 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1514 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1518 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1520 Gdk::Cursor* cursor;
1521 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1522 float x_fraction = cnote->mouse_x_fraction ();
1524 if (x_fraction > 0.0 && x_fraction < 0.25) {
1525 cursor = _editor->cursors()->left_side_trim;
1527 cursor = _editor->cursors()->right_side_trim;
1530 Drag::start_grab (event, cursor);
1532 region = &cnote->region_view();
1534 double const region_start = region->get_position_pixels();
1535 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1537 if (grab_x() <= middle_point) {
1538 cursor = _editor->cursors()->left_side_trim;
1541 cursor = _editor->cursors()->right_side_trim;
1545 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1547 if (event->motion.state & Keyboard::PrimaryModifier) {
1553 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1555 if (ms.size() > 1) {
1556 /* has to be relative, may make no sense otherwise */
1560 /* select this note; if it is already selected, preserve the existing selection,
1561 otherwise make this note the only one selected.
1563 region->note_selected (cnote, cnote->selected ());
1565 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1566 MidiRegionSelection::iterator next;
1569 (*r)->begin_resizing (at_front);
1575 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1577 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1578 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1579 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1584 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1586 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1587 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1588 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1593 NoteResizeDrag::aborted (bool)
1595 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1596 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1597 (*r)->abort_resizing ();
1601 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1604 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1608 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1614 RegionGainDrag::finished (GdkEvent *, bool)
1620 RegionGainDrag::aborted (bool)
1625 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1626 : RegionDrag (e, i, p, v)
1628 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1632 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1635 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1636 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1638 if (tv && tv->is_track()) {
1639 speed = tv->track()->speed();
1642 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1643 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1644 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1646 framepos_t const pf = adjusted_current_frame (event);
1648 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1649 /* Move the contents of the region around without changing the region bounds */
1650 _operation = ContentsTrim;
1651 Drag::start_grab (event, _editor->cursors()->trimmer);
1653 /* These will get overridden for a point trim.*/
1654 if (pf < (region_start + region_length/2)) {
1655 /* closer to front */
1656 _operation = StartTrim;
1657 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1660 _operation = EndTrim;
1661 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1665 switch (_operation) {
1667 show_verbose_cursor_time (region_start);
1668 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1669 i->view->trim_front_starting ();
1673 show_verbose_cursor_time (region_end);
1676 show_verbose_cursor_time (pf);
1680 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1681 i->view->region()->suspend_property_changes ();
1686 TrimDrag::motion (GdkEvent* event, bool first_move)
1688 RegionView* rv = _primary;
1691 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1692 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1693 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1695 if (tv && tv->is_track()) {
1696 speed = tv->track()->speed();
1699 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1705 switch (_operation) {
1707 trim_type = "Region start trim";
1710 trim_type = "Region end trim";
1713 trim_type = "Region content trim";
1717 _editor->begin_reversible_command (trim_type);
1719 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1720 RegionView* rv = i->view;
1721 rv->fake_set_opaque (false);
1722 rv->enable_display (false);
1723 rv->region()->playlist()->clear_owned_changes ();
1725 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1728 arv->temporarily_hide_envelope ();
1731 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1732 insert_result = _editor->motion_frozen_playlists.insert (pl);
1734 if (insert_result.second) {
1740 bool non_overlap_trim = false;
1742 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1743 non_overlap_trim = true;
1746 switch (_operation) {
1748 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1749 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1754 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1755 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1761 bool swap_direction = false;
1763 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1764 swap_direction = true;
1767 framecnt_t frame_delta = 0;
1769 bool left_direction = false;
1770 if (last_pointer_frame() > adjusted_current_frame(event)) {
1771 left_direction = true;
1774 if (left_direction) {
1775 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1777 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1780 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1781 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1787 switch (_operation) {
1789 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1792 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1795 show_verbose_cursor_time (adjusted_current_frame (event));
1802 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1804 if (movement_occurred) {
1805 motion (event, false);
1807 /* This must happen before the region's StatefulDiffCommand is created, as it may
1808 `correct' (ahem) the region's _start from being negative to being zero. It
1809 needs to be zero in the undo record.
1811 if (_operation == StartTrim) {
1812 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1813 i->view->trim_front_ending ();
1817 if (!_editor->selection->selected (_primary)) {
1818 _primary->thaw_after_trim ();
1821 set<boost::shared_ptr<Playlist> > diffed_playlists;
1823 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1824 i->view->thaw_after_trim ();
1825 i->view->enable_display (true);
1826 i->view->fake_set_opaque (true);
1828 /* Trimming one region may affect others on the playlist, so we need
1829 to get undo Commands from the whole playlist rather than just the
1830 region. Use diffed_playlists to make sure we don't diff a given
1831 playlist more than once.
1833 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1834 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1835 vector<Command*> cmds;
1837 _editor->session()->add_commands (cmds);
1838 diffed_playlists.insert (p);
1842 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1846 _editor->motion_frozen_playlists.clear ();
1847 _editor->commit_reversible_command();
1850 /* no mouse movement */
1851 _editor->point_trim (event, adjusted_current_frame (event));
1854 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1855 if (_operation == StartTrim) {
1856 i->view->trim_front_ending ();
1859 i->view->region()->resume_property_changes ();
1864 TrimDrag::aborted (bool movement_occurred)
1866 /* Our motion method is changing model state, so use the Undo system
1867 to cancel. Perhaps not ideal, as this will leave an Undo point
1868 behind which may be slightly odd from the user's point of view.
1873 if (movement_occurred) {
1877 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1878 i->view->region()->resume_property_changes ();
1883 TrimDrag::setup_pointer_frame_offset ()
1885 list<DraggingView>::iterator i = _views.begin ();
1886 while (i != _views.end() && i->view != _primary) {
1890 if (i == _views.end()) {
1894 switch (_operation) {
1896 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1899 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1906 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1910 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1911 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1916 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1918 Drag::start_grab (event, cursor);
1919 show_verbose_cursor_time (adjusted_current_frame(event));
1923 MeterMarkerDrag::setup_pointer_frame_offset ()
1925 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1929 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1933 // create a dummy marker for visual representation of moving the
1934 // section, because whether its a copy or not, we're going to
1935 // leave or lose the original marker (leave if its a copy; lose if its
1936 // not, because we'll remove it from the map).
1938 MeterSection section (_marker->meter());
1940 if (!section.movable()) {
1945 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1947 _marker = new MeterMarker (
1949 *_editor->meter_group,
1950 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1952 *new MeterSection (_marker->meter())
1955 /* use the new marker for the grab */
1956 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1959 TempoMap& map (_editor->session()->tempo_map());
1960 /* get current state */
1961 before_state = &map.get_state();
1962 /* remove the section while we drag it */
1963 map.remove_meter (section, true);
1967 framepos_t const pf = adjusted_current_frame (event);
1968 _marker->set_position (pf);
1969 show_verbose_cursor_time (pf);
1973 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1975 if (!movement_occurred) {
1979 motion (event, false);
1981 Timecode::BBT_Time when;
1983 TempoMap& map (_editor->session()->tempo_map());
1984 map.bbt_time (last_pointer_frame(), when);
1986 if (_copy == true) {
1987 _editor->begin_reversible_command (_("copy meter mark"));
1988 XMLNode &before = map.get_state();
1989 map.add_meter (_marker->meter(), when);
1990 XMLNode &after = map.get_state();
1991 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1992 _editor->commit_reversible_command ();
1995 _editor->begin_reversible_command (_("move meter mark"));
1997 /* we removed it before, so add it back now */
1999 map.add_meter (_marker->meter(), when);
2000 XMLNode &after = map.get_state();
2001 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2002 _editor->commit_reversible_command ();
2005 // delete the dummy marker we used for visual representation while moving.
2006 // a new visual marker will show up automatically.
2011 MeterMarkerDrag::aborted (bool moved)
2013 _marker->set_position (_marker->meter().frame ());
2016 TempoMap& map (_editor->session()->tempo_map());
2017 /* we removed it before, so add it back now */
2018 map.add_meter (_marker->meter(), _marker->meter().frame());
2019 // delete the dummy marker we used for visual representation while moving.
2020 // a new visual marker will show up automatically.
2025 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2029 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2031 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2036 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2038 Drag::start_grab (event, cursor);
2039 show_verbose_cursor_time (adjusted_current_frame (event));
2043 TempoMarkerDrag::setup_pointer_frame_offset ()
2045 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2049 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2053 // create a dummy marker for visual representation of moving the
2054 // section, because whether its a copy or not, we're going to
2055 // leave or lose the original marker (leave if its a copy; lose if its
2056 // not, because we'll remove it from the map).
2058 // create a dummy marker for visual representation of moving the copy.
2059 // The actual copying is not done before we reach the finish callback.
2062 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2064 TempoSection section (_marker->tempo());
2066 _marker = new TempoMarker (
2068 *_editor->tempo_group,
2069 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2071 *new TempoSection (_marker->tempo())
2074 /* use the new marker for the grab */
2075 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2078 TempoMap& map (_editor->session()->tempo_map());
2079 /* get current state */
2080 before_state = &map.get_state();
2081 /* remove the section while we drag it */
2082 map.remove_tempo (section, true);
2086 framepos_t const pf = adjusted_current_frame (event);
2087 _marker->set_position (pf);
2088 show_verbose_cursor_time (pf);
2092 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2094 if (!movement_occurred) {
2098 motion (event, false);
2100 TempoMap& map (_editor->session()->tempo_map());
2101 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2102 Timecode::BBT_Time when;
2104 map.bbt_time (beat_time, when);
2106 if (_copy == true) {
2107 _editor->begin_reversible_command (_("copy tempo mark"));
2108 XMLNode &before = map.get_state();
2109 map.add_tempo (_marker->tempo(), when);
2110 XMLNode &after = map.get_state();
2111 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2112 _editor->commit_reversible_command ();
2115 _editor->begin_reversible_command (_("move tempo mark"));
2116 /* we removed it before, so add it back now */
2117 map.add_tempo (_marker->tempo(), when);
2118 XMLNode &after = map.get_state();
2119 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2120 _editor->commit_reversible_command ();
2123 // delete the dummy marker we used for visual representation while moving.
2124 // a new visual marker will show up automatically.
2129 TempoMarkerDrag::aborted (bool moved)
2131 _marker->set_position (_marker->tempo().frame());
2133 TempoMap& map (_editor->session()->tempo_map());
2134 /* we removed it before, so add it back now */
2135 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2136 // delete the dummy marker we used for visual representation while moving.
2137 // a new visual marker will show up automatically.
2142 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2146 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2149 /** Do all the things we do when dragging the playhead to make it look as though
2150 * we have located, without actually doing the locate (because that would cause
2151 * the diskstream buffers to be refilled, which is too slow).
2154 CursorDrag::fake_locate (framepos_t t)
2156 _editor->playhead_cursor->set_position (t);
2158 Session* s = _editor->session ();
2159 if (s->timecode_transmission_suspended ()) {
2160 framepos_t const f = _editor->playhead_cursor->current_frame;
2161 s->send_mmc_locate (f);
2162 s->send_full_time_code (f);
2165 show_verbose_cursor_time (t);
2166 _editor->UpdateAllTransportClocks (t);
2170 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2172 Drag::start_grab (event, c);
2174 _grab_zoom = _editor->frames_per_unit;
2176 framepos_t where = _editor->event_frame (event, 0, 0);
2177 _editor->snap_to_with_modifier (where, event);
2179 _editor->_dragging_playhead = true;
2181 Session* s = _editor->session ();
2184 if (_was_rolling && _stop) {
2188 if (s->is_auditioning()) {
2189 s->cancel_audition ();
2192 s->request_suspend_timecode_transmission ();
2193 while (!s->timecode_transmission_suspended ()) {
2194 /* twiddle our thumbs */
2198 fake_locate (where);
2202 CursorDrag::motion (GdkEvent* event, bool)
2204 framepos_t const adjusted_frame = adjusted_current_frame (event);
2205 if (adjusted_frame != last_pointer_frame()) {
2206 fake_locate (adjusted_frame);
2208 _editor->update_canvas_now ();
2214 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2216 _editor->_dragging_playhead = false;
2218 if (!movement_occurred && _stop) {
2222 motion (event, false);
2224 Session* s = _editor->session ();
2226 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2227 _editor->_pending_locate_request = true;
2228 s->request_resume_timecode_transmission ();
2233 CursorDrag::aborted (bool)
2235 if (_editor->_dragging_playhead) {
2236 _editor->session()->request_resume_timecode_transmission ();
2237 _editor->_dragging_playhead = false;
2240 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2243 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2244 : RegionDrag (e, i, p, v)
2246 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2250 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2252 Drag::start_grab (event, cursor);
2254 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2255 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2257 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2259 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2263 FadeInDrag::setup_pointer_frame_offset ()
2265 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2266 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2267 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2271 FadeInDrag::motion (GdkEvent* event, bool)
2273 framecnt_t fade_length;
2275 framepos_t const pos = adjusted_current_frame (event);
2277 boost::shared_ptr<Region> region = _primary->region ();
2279 if (pos < (region->position() + 64)) {
2280 fade_length = 64; // this should be a minimum defined somewhere
2281 } else if (pos > region->last_frame()) {
2282 fade_length = region->length();
2284 fade_length = pos - region->position();
2287 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2289 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2295 tmp->reset_fade_in_shape_width (fade_length);
2296 tmp->show_fade_line((framecnt_t) fade_length);
2299 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2303 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2305 if (!movement_occurred) {
2309 framecnt_t fade_length;
2311 framepos_t const pos = adjusted_current_frame (event);
2313 boost::shared_ptr<Region> region = _primary->region ();
2315 if (pos < (region->position() + 64)) {
2316 fade_length = 64; // this should be a minimum defined somewhere
2317 } else if (pos > region->last_frame()) {
2318 fade_length = region->length();
2320 fade_length = pos - region->position();
2323 _editor->begin_reversible_command (_("change fade in length"));
2325 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2327 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2333 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2334 XMLNode &before = alist->get_state();
2336 tmp->audio_region()->set_fade_in_length (fade_length);
2337 tmp->audio_region()->set_fade_in_active (true);
2338 tmp->hide_fade_line();
2340 XMLNode &after = alist->get_state();
2341 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2344 _editor->commit_reversible_command ();
2348 FadeInDrag::aborted (bool)
2350 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2351 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2357 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2358 tmp->hide_fade_line();
2362 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2363 : RegionDrag (e, i, p, v)
2365 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2369 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2371 Drag::start_grab (event, cursor);
2373 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2374 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2376 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2378 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2382 FadeOutDrag::setup_pointer_frame_offset ()
2384 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2385 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2386 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2390 FadeOutDrag::motion (GdkEvent* event, bool)
2392 framecnt_t fade_length;
2394 framepos_t const pos = adjusted_current_frame (event);
2396 boost::shared_ptr<Region> region = _primary->region ();
2398 if (pos > (region->last_frame() - 64)) {
2399 fade_length = 64; // this should really be a minimum fade defined somewhere
2401 else if (pos < region->position()) {
2402 fade_length = region->length();
2405 fade_length = region->last_frame() - pos;
2408 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2410 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2416 tmp->reset_fade_out_shape_width (fade_length);
2417 tmp->show_fade_line(region->length() - fade_length);
2420 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2424 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2426 if (!movement_occurred) {
2430 framecnt_t fade_length;
2432 framepos_t const pos = adjusted_current_frame (event);
2434 boost::shared_ptr<Region> region = _primary->region ();
2436 if (pos > (region->last_frame() - 64)) {
2437 fade_length = 64; // this should really be a minimum fade defined somewhere
2439 else if (pos < region->position()) {
2440 fade_length = region->length();
2443 fade_length = region->last_frame() - pos;
2446 _editor->begin_reversible_command (_("change fade out length"));
2448 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2450 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2456 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2457 XMLNode &before = alist->get_state();
2459 tmp->audio_region()->set_fade_out_length (fade_length);
2460 tmp->audio_region()->set_fade_out_active (true);
2461 tmp->hide_fade_line();
2463 XMLNode &after = alist->get_state();
2464 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2467 _editor->commit_reversible_command ();
2471 FadeOutDrag::aborted (bool)
2473 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2474 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2480 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2481 tmp->hide_fade_line();
2485 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2488 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2490 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2493 _points.push_back (Gnome::Art::Point (0, 0));
2494 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2497 MarkerDrag::~MarkerDrag ()
2499 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2505 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2507 Drag::start_grab (event, cursor);
2511 Location *location = _editor->find_location_from_marker (_marker, is_start);
2512 _editor->_dragging_edit_point = true;
2514 update_item (location);
2516 // _drag_line->show();
2517 // _line->raise_to_top();
2520 show_verbose_cursor_time (location->start());
2522 show_verbose_cursor_time (location->end());
2525 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2528 case Selection::Toggle:
2529 _editor->selection->toggle (_marker);
2531 case Selection::Set:
2532 if (!_editor->selection->selected (_marker)) {
2533 _editor->selection->set (_marker);
2536 case Selection::Extend:
2538 Locations::LocationList ll;
2539 list<Marker*> to_add;
2541 _editor->selection->markers.range (s, e);
2542 s = min (_marker->position(), s);
2543 e = max (_marker->position(), e);
2546 if (e < max_framepos) {
2549 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2550 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2551 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2554 to_add.push_back (lm->start);
2557 to_add.push_back (lm->end);
2561 if (!to_add.empty()) {
2562 _editor->selection->add (to_add);
2566 case Selection::Add:
2567 _editor->selection->add (_marker);
2571 /* Set up copies for us to manipulate during the drag */
2573 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2574 Location* l = _editor->find_location_from_marker (*i, is_start);
2575 _copied_locations.push_back (new Location (*l));
2580 MarkerDrag::setup_pointer_frame_offset ()
2583 Location *location = _editor->find_location_from_marker (_marker, is_start);
2584 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2588 MarkerDrag::motion (GdkEvent* event, bool)
2590 framecnt_t f_delta = 0;
2592 bool move_both = false;
2594 Location *real_location;
2595 Location *copy_location = 0;
2597 framepos_t const newframe = adjusted_current_frame (event);
2599 framepos_t next = newframe;
2601 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2605 MarkerSelection::iterator i;
2606 list<Location*>::iterator x;
2608 /* find the marker we're dragging, and compute the delta */
2610 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2611 x != _copied_locations.end() && i != _editor->selection->markers.end();
2617 if (marker == _marker) {
2619 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2624 if (real_location->is_mark()) {
2625 f_delta = newframe - copy_location->start();
2629 switch (marker->type()) {
2630 case Marker::SessionStart:
2631 case Marker::RangeStart:
2632 case Marker::LoopStart:
2633 case Marker::PunchIn:
2634 f_delta = newframe - copy_location->start();
2637 case Marker::SessionEnd:
2638 case Marker::RangeEnd:
2639 case Marker::LoopEnd:
2640 case Marker::PunchOut:
2641 f_delta = newframe - copy_location->end();
2644 /* what kind of marker is this ? */
2652 if (i == _editor->selection->markers.end()) {
2653 /* hmm, impossible - we didn't find the dragged marker */
2657 /* now move them all */
2659 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2660 x != _copied_locations.end() && i != _editor->selection->markers.end();
2666 /* call this to find out if its the start or end */
2668 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2672 if (real_location->locked()) {
2676 if (copy_location->is_mark()) {
2680 copy_location->set_start (copy_location->start() + f_delta);
2684 framepos_t new_start = copy_location->start() + f_delta;
2685 framepos_t new_end = copy_location->end() + f_delta;
2687 if (is_start) { // start-of-range marker
2690 copy_location->set_start (new_start);
2691 copy_location->set_end (new_end);
2692 } else if (new_start < copy_location->end()) {
2693 copy_location->set_start (new_start);
2694 } else if (newframe > 0) {
2695 _editor->snap_to (next, 1, true);
2696 copy_location->set_end (next);
2697 copy_location->set_start (newframe);
2700 } else { // end marker
2703 copy_location->set_end (new_end);
2704 copy_location->set_start (new_start);
2705 } else if (new_end > copy_location->start()) {
2706 copy_location->set_end (new_end);
2707 } else if (newframe > 0) {
2708 _editor->snap_to (next, -1, true);
2709 copy_location->set_start (next);
2710 copy_location->set_end (newframe);
2715 update_item (copy_location);
2717 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2720 lm->set_position (copy_location->start(), copy_location->end());
2724 assert (!_copied_locations.empty());
2726 show_verbose_cursor_time (newframe);
2729 _editor->update_canvas_now ();
2734 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2736 if (!movement_occurred) {
2738 /* just a click, do nothing but finish
2739 off the selection process
2742 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2745 case Selection::Set:
2746 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2747 _editor->selection->set (_marker);
2751 case Selection::Toggle:
2752 case Selection::Extend:
2753 case Selection::Add:
2760 _editor->_dragging_edit_point = false;
2762 _editor->begin_reversible_command ( _("move marker") );
2763 XMLNode &before = _editor->session()->locations()->get_state();
2765 MarkerSelection::iterator i;
2766 list<Location*>::iterator x;
2769 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2770 x != _copied_locations.end() && i != _editor->selection->markers.end();
2773 Location * location = _editor->find_location_from_marker (*i, is_start);
2777 if (location->locked()) {
2781 if (location->is_mark()) {
2782 location->set_start ((*x)->start());
2784 location->set ((*x)->start(), (*x)->end());
2789 XMLNode &after = _editor->session()->locations()->get_state();
2790 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2791 _editor->commit_reversible_command ();
2795 MarkerDrag::aborted (bool)
2801 MarkerDrag::update_item (Location*)
2806 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2808 _cumulative_x_drag (0),
2809 _cumulative_y_drag (0)
2811 if (_zero_gain_fraction < 0.0) {
2812 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2815 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2817 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2823 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2825 Drag::start_grab (event, _editor->cursors()->fader);
2827 // start the grab at the center of the control point so
2828 // the point doesn't 'jump' to the mouse after the first drag
2829 _fixed_grab_x = _point->get_x();
2830 _fixed_grab_y = _point->get_y();
2832 float const fraction = 1 - (_point->get_y() / _point->line().height());
2834 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2836 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2837 event->button.x + 10, event->button.y + 10);
2839 _editor->verbose_cursor()->show ();
2843 ControlPointDrag::motion (GdkEvent* event, bool)
2845 double dx = _drags->current_pointer_x() - last_pointer_x();
2846 double dy = _drags->current_pointer_y() - last_pointer_y();
2848 if (event->button.state & Keyboard::SecondaryModifier) {
2853 /* coordinate in pixels relative to the start of the region (for region-based automation)
2854 or track (for track-based automation) */
2855 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2856 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2858 // calculate zero crossing point. back off by .01 to stay on the
2859 // positive side of zero
2860 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2862 // make sure we hit zero when passing through
2863 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2867 if (_x_constrained) {
2870 if (_y_constrained) {
2874 _cumulative_x_drag = cx - _fixed_grab_x;
2875 _cumulative_y_drag = cy - _fixed_grab_y;
2879 cy = min ((double) _point->line().height(), cy);
2881 framepos_t cx_frames = _editor->unit_to_frame (cx);
2883 if (!_x_constrained) {
2884 _editor->snap_to_with_modifier (cx_frames, event);
2887 cx_frames = min (cx_frames, _point->line().maximum_time());
2889 float const fraction = 1.0 - (cy / _point->line().height());
2891 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2893 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2895 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2899 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2901 if (!movement_occurred) {
2905 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2906 _editor->reset_point_selection ();
2910 motion (event, false);
2913 _point->line().end_drag ();
2914 _editor->session()->commit_reversible_command ();
2918 ControlPointDrag::aborted (bool)
2920 _point->line().reset ();
2924 ControlPointDrag::active (Editing::MouseMode m)
2926 if (m == Editing::MouseGain) {
2927 /* always active in mouse gain */
2931 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2932 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2935 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2938 _cumulative_y_drag (0)
2940 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2944 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2946 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2949 _item = &_line->grab_item ();
2951 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2952 origin, and ditto for y.
2955 double cx = event->button.x;
2956 double cy = event->button.y;
2958 _line->parent_group().w2i (cx, cy);
2960 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2965 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2966 /* no adjacent points */
2970 Drag::start_grab (event, _editor->cursors()->fader);
2972 /* store grab start in parent frame */
2977 double fraction = 1.0 - (cy / _line->height());
2979 _line->start_drag_line (before, after, fraction);
2981 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2982 event->button.x + 10, event->button.y + 10);
2984 _editor->verbose_cursor()->show ();
2988 LineDrag::motion (GdkEvent* event, bool)
2990 double dy = _drags->current_pointer_y() - last_pointer_y();
2992 if (event->button.state & Keyboard::SecondaryModifier) {
2996 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2998 _cumulative_y_drag = cy - _fixed_grab_y;
3001 cy = min ((double) _line->height(), cy);
3003 double const fraction = 1.0 - (cy / _line->height());
3007 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3013 /* we are ignoring x position for this drag, so we can just pass in anything */
3014 _line->drag_motion (0, fraction, true, push);
3016 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3020 LineDrag::finished (GdkEvent* event, bool)
3022 motion (event, false);
3024 _editor->session()->commit_reversible_command ();
3028 LineDrag::aborted (bool)
3033 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3036 _cumulative_x_drag (0)
3038 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3042 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3044 Drag::start_grab (event);
3046 _line = reinterpret_cast<Line*> (_item);
3049 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3051 double cx = event->button.x;
3052 double cy = event->button.y;
3054 _item->property_parent().get_value()->w2i(cx, cy);
3056 /* store grab start in parent frame */
3057 _region_view_grab_x = cx;
3059 _before = *(float*) _item->get_data ("position");
3061 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3063 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3067 FeatureLineDrag::motion (GdkEvent*, bool)
3069 double dx = _drags->current_pointer_x() - last_pointer_x();
3071 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3073 _cumulative_x_drag += dx;
3075 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3084 ArdourCanvas::Points points;
3086 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3088 _line->get_bounds(x1, y2, x2, y2);
3090 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3091 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3093 _line->property_points() = points;
3095 float *pos = new float;
3098 _line->set_data ("position", pos);
3104 FeatureLineDrag::finished (GdkEvent*, bool)
3106 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3107 _arv->update_transient(_before, _before);
3111 FeatureLineDrag::aborted (bool)
3116 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3118 , _vertical_only (false)
3120 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3124 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3126 Drag::start_grab (event);
3127 show_verbose_cursor_time (adjusted_current_frame (event));
3131 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3138 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3140 framepos_t grab = grab_frame ();
3141 if (Config->get_rubberbanding_snaps_to_grid ()) {
3142 _editor->snap_to_with_modifier (grab, event);
3145 /* base start and end on initial click position */
3155 if (_drags->current_pointer_y() < grab_y()) {
3156 y1 = _drags->current_pointer_y();
3159 y2 = _drags->current_pointer_y();
3164 if (start != end || y1 != y2) {
3166 double x1 = _editor->frame_to_pixel (start);
3167 double x2 = _editor->frame_to_pixel (end);
3169 _editor->rubberband_rect->property_x1() = x1;
3170 if (_vertical_only) {
3171 /* fixed 10 pixel width */
3172 _editor->rubberband_rect->property_x2() = x1 + 10;
3174 _editor->rubberband_rect->property_x2() = x2;
3177 _editor->rubberband_rect->property_y1() = y1;
3178 _editor->rubberband_rect->property_y2() = y2;
3180 _editor->rubberband_rect->show();
3181 _editor->rubberband_rect->raise_to_top();
3183 show_verbose_cursor_time (pf);
3185 do_select_things (event, true);
3190 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3195 if (grab_frame() < last_pointer_frame()) {
3197 x2 = last_pointer_frame ();
3200 x1 = last_pointer_frame ();
3206 if (_drags->current_pointer_y() < grab_y()) {
3207 y1 = _drags->current_pointer_y();
3210 y2 = _drags->current_pointer_y();
3214 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3218 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3220 if (movement_occurred) {
3222 motion (event, false);
3223 do_select_things (event, false);
3229 bool do_deselect = true;
3230 MidiTimeAxisView* mtv;
3232 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3234 if (_editor->selection->empty()) {
3235 /* nothing selected */
3236 add_midi_region (mtv);
3237 do_deselect = false;
3247 _editor->rubberband_rect->hide();
3251 RubberbandSelectDrag::aborted (bool)
3253 _editor->rubberband_rect->hide ();
3256 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3257 : RegionDrag (e, i, p, v)
3259 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3263 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3265 Drag::start_grab (event, cursor);
3267 show_verbose_cursor_time (adjusted_current_frame (event));
3271 TimeFXDrag::motion (GdkEvent* event, bool)
3273 RegionView* rv = _primary;
3275 framepos_t const pf = adjusted_current_frame (event);
3277 if (pf > rv->region()->position()) {
3278 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3281 show_verbose_cursor_time (pf);
3285 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3287 _primary->get_time_axis_view().hide_timestretch ();
3289 if (!movement_occurred) {
3293 if (last_pointer_frame() < _primary->region()->position()) {
3294 /* backwards drag of the left edge - not usable */
3298 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3300 float percentage = (double) newlen / (double) _primary->region()->length();
3302 #ifndef USE_RUBBERBAND
3303 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3304 if (_primary->region()->data_type() == DataType::AUDIO) {
3305 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3309 if (!_editor->get_selection().regions.empty()) {
3310 /* primary will already be included in the selection, and edit
3311 group shared editing will propagate selection across
3312 equivalent regions, so just use the current region
3316 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3317 error << _("An error occurred while executing time stretch operation") << endmsg;
3323 TimeFXDrag::aborted (bool)
3325 _primary->get_time_axis_view().hide_timestretch ();
3328 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3331 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3335 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3337 Drag::start_grab (event);
3341 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3343 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3347 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3349 if (movement_occurred && _editor->session()) {
3350 /* make sure we stop */
3351 _editor->session()->request_transport_speed (0.0);
3356 ScrubDrag::aborted (bool)
3361 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3365 , _original_pointer_time_axis (-1)
3366 , _last_pointer_time_axis (-1)
3368 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3372 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3374 if (_editor->session() == 0) {
3378 Gdk::Cursor* cursor = 0;
3380 switch (_operation) {
3381 case CreateSelection:
3382 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3387 cursor = _editor->cursors()->selector;
3388 Drag::start_grab (event, cursor);
3391 case SelectionStartTrim:
3392 if (_editor->clicked_axisview) {
3393 _editor->clicked_axisview->order_selection_trims (_item, true);
3395 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3398 case SelectionEndTrim:
3399 if (_editor->clicked_axisview) {
3400 _editor->clicked_axisview->order_selection_trims (_item, false);
3402 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3406 Drag::start_grab (event, cursor);
3410 if (_operation == SelectionMove) {
3411 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3413 show_verbose_cursor_time (adjusted_current_frame (event));
3416 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3420 SelectionDrag::setup_pointer_frame_offset ()
3422 switch (_operation) {
3423 case CreateSelection:
3424 _pointer_frame_offset = 0;
3427 case SelectionStartTrim:
3429 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3432 case SelectionEndTrim:
3433 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3439 SelectionDrag::motion (GdkEvent* event, bool first_move)
3441 framepos_t start = 0;
3445 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3446 if (pending_time_axis.first == 0) {
3450 framepos_t const pending_position = adjusted_current_frame (event);
3452 /* only alter selection if things have changed */
3454 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3458 switch (_operation) {
3459 case CreateSelection:
3461 framepos_t grab = grab_frame ();
3464 _editor->snap_to (grab);
3467 if (pending_position < grab_frame()) {
3468 start = pending_position;
3471 end = pending_position;
3475 /* first drag: Either add to the selection
3476 or create a new selection
3482 /* adding to the selection */
3483 _editor->set_selected_track_as_side_effect (Selection::Add);
3484 //_editor->selection->add (_editor->clicked_axisview);
3485 _editor->clicked_selection = _editor->selection->add (start, end);
3490 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3491 //_editor->selection->set (_editor->clicked_axisview);
3492 _editor->set_selected_track_as_side_effect (Selection::Set);
3495 _editor->clicked_selection = _editor->selection->set (start, end);
3499 /* select the track that we're in */
3500 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3501 // _editor->set_selected_track_as_side_effect (Selection::Add);
3502 _editor->selection->add (pending_time_axis.first);
3503 _added_time_axes.push_back (pending_time_axis.first);
3506 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3507 tracks that we selected in the first place.
3510 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3511 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3513 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3514 while (i != _added_time_axes.end()) {
3516 list<TimeAxisView*>::iterator tmp = i;
3519 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3520 _editor->selection->remove (*i);
3521 _added_time_axes.remove (*i);
3530 case SelectionStartTrim:
3532 start = _editor->selection->time[_editor->clicked_selection].start;
3533 end = _editor->selection->time[_editor->clicked_selection].end;
3535 if (pending_position > end) {
3538 start = pending_position;
3542 case SelectionEndTrim:
3544 start = _editor->selection->time[_editor->clicked_selection].start;
3545 end = _editor->selection->time[_editor->clicked_selection].end;
3547 if (pending_position < start) {
3550 end = pending_position;
3557 start = _editor->selection->time[_editor->clicked_selection].start;
3558 end = _editor->selection->time[_editor->clicked_selection].end;
3560 length = end - start;
3562 start = pending_position;
3563 _editor->snap_to (start);
3565 end = start + length;
3570 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3571 _editor->start_canvas_autoscroll (1, 0);
3575 _editor->selection->replace (_editor->clicked_selection, start, end);
3578 if (_operation == SelectionMove) {
3579 show_verbose_cursor_time(start);
3581 show_verbose_cursor_time(pending_position);
3586 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3588 Session* s = _editor->session();
3590 if (movement_occurred) {
3591 motion (event, false);
3592 /* XXX this is not object-oriented programming at all. ick */
3593 if (_editor->selection->time.consolidate()) {
3594 _editor->selection->TimeChanged ();
3597 /* XXX what if its a music time selection? */
3598 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3599 s->request_play_range (&_editor->selection->time, true);
3604 /* just a click, no pointer movement.*/
3606 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3607 _editor->selection->clear_time();
3610 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3611 _editor->selection->set (_editor->clicked_axisview);
3614 if (s && s->get_play_range () && s->transport_rolling()) {
3615 s->request_stop (false, false);
3620 _editor->stop_canvas_autoscroll ();
3624 SelectionDrag::aborted (bool)
3629 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3634 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3636 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3637 physical_screen_height (_editor->get_window()));
3638 _drag_rect->hide ();
3640 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3641 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3645 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3647 if (_editor->session() == 0) {
3651 Gdk::Cursor* cursor = 0;
3653 if (!_editor->temp_location) {
3654 _editor->temp_location = new Location (*_editor->session());
3657 switch (_operation) {
3658 case CreateRangeMarker:
3659 case CreateTransportMarker:
3660 case CreateCDMarker:
3662 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3667 cursor = _editor->cursors()->selector;
3671 Drag::start_grab (event, cursor);
3673 show_verbose_cursor_time (adjusted_current_frame (event));
3677 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3679 framepos_t start = 0;
3681 ArdourCanvas::SimpleRect *crect;
3683 switch (_operation) {
3684 case CreateRangeMarker:
3685 crect = _editor->range_bar_drag_rect;
3687 case CreateTransportMarker:
3688 crect = _editor->transport_bar_drag_rect;
3690 case CreateCDMarker:
3691 crect = _editor->cd_marker_bar_drag_rect;
3694 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3699 framepos_t const pf = adjusted_current_frame (event);
3701 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3702 framepos_t grab = grab_frame ();
3703 _editor->snap_to (grab);
3705 if (pf < grab_frame()) {
3713 /* first drag: Either add to the selection
3714 or create a new selection.
3719 _editor->temp_location->set (start, end);
3723 update_item (_editor->temp_location);
3725 //_drag_rect->raise_to_top();
3730 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3731 _editor->start_canvas_autoscroll (1, 0);
3735 _editor->temp_location->set (start, end);
3737 double x1 = _editor->frame_to_pixel (start);
3738 double x2 = _editor->frame_to_pixel (end);
3739 crect->property_x1() = x1;
3740 crect->property_x2() = x2;
3742 update_item (_editor->temp_location);
3745 show_verbose_cursor_time (pf);
3750 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3752 Location * newloc = 0;
3756 if (movement_occurred) {
3757 motion (event, false);
3760 switch (_operation) {
3761 case CreateRangeMarker:
3762 case CreateCDMarker:
3764 _editor->begin_reversible_command (_("new range marker"));
3765 XMLNode &before = _editor->session()->locations()->get_state();
3766 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3767 if (_operation == CreateCDMarker) {
3768 flags = Location::IsRangeMarker | Location::IsCDMarker;
3769 _editor->cd_marker_bar_drag_rect->hide();
3772 flags = Location::IsRangeMarker;
3773 _editor->range_bar_drag_rect->hide();
3775 newloc = new Location (
3776 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3779 _editor->session()->locations()->add (newloc, true);
3780 XMLNode &after = _editor->session()->locations()->get_state();
3781 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3782 _editor->commit_reversible_command ();
3786 case CreateTransportMarker:
3787 // popup menu to pick loop or punch
3788 _editor->new_transport_marker_context_menu (&event->button, _item);
3792 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3794 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3799 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3801 if (end == max_framepos) {
3802 end = _editor->session()->current_end_frame ();
3805 if (start == max_framepos) {
3806 start = _editor->session()->current_start_frame ();
3809 switch (_editor->mouse_mode) {
3811 /* find the two markers on either side and then make the selection from it */
3812 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3816 /* find the two markers on either side of the click and make the range out of it */
3817 _editor->selection->set (start, end);
3826 _editor->stop_canvas_autoscroll ();
3830 RangeMarkerBarDrag::aborted (bool)
3836 RangeMarkerBarDrag::update_item (Location* location)
3838 double const x1 = _editor->frame_to_pixel (location->start());
3839 double const x2 = _editor->frame_to_pixel (location->end());
3841 _drag_rect->property_x1() = x1;
3842 _drag_rect->property_x2() = x2;
3845 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3849 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3853 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3855 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3856 Drag::start_grab (event, _editor->cursors()->zoom_out);
3859 Drag::start_grab (event, _editor->cursors()->zoom_in);
3863 show_verbose_cursor_time (adjusted_current_frame (event));
3867 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3872 framepos_t const pf = adjusted_current_frame (event);
3874 framepos_t grab = grab_frame ();
3875 _editor->snap_to_with_modifier (grab, event);
3877 /* base start and end on initial click position */
3889 _editor->zoom_rect->show();
3890 _editor->zoom_rect->raise_to_top();
3893 _editor->reposition_zoom_rect(start, end);
3895 show_verbose_cursor_time (pf);
3900 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3902 if (movement_occurred) {
3903 motion (event, false);
3905 if (grab_frame() < last_pointer_frame()) {
3906 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3908 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3911 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3912 _editor->tav_zoom_step (_zoom_out);
3914 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3918 _editor->zoom_rect->hide();
3922 MouseZoomDrag::aborted (bool)
3924 _editor->zoom_rect->hide ();
3927 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3929 , _cumulative_dx (0)
3930 , _cumulative_dy (0)
3932 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3934 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3935 _region = &_primary->region_view ();
3936 _note_height = _region->midi_stream_view()->note_height ();
3940 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3942 Drag::start_grab (event);
3944 if (!(_was_selected = _primary->selected())) {
3946 /* tertiary-click means extend selection - we'll do that on button release,
3947 so don't add it here, because otherwise we make it hard to figure
3948 out the "extend-to" range.
3951 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3954 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3957 _region->note_selected (_primary, true);
3959 _region->unique_select (_primary);
3965 /** @return Current total drag x change in frames */
3967 NoteDrag::total_dx () const
3970 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3972 /* primary note time */
3973 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3975 /* new time of the primary note in session frames */
3976 frameoffset_t st = n + dx;
3978 framepos_t const rp = _region->region()->position ();
3980 /* prevent the note being dragged earlier than the region's position */
3983 /* snap and return corresponding delta */
3984 return _region->snap_frame_to_frame (st - rp) + rp - n;
3987 /** @return Current total drag y change in note number */
3989 NoteDrag::total_dy () const
3991 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3995 NoteDrag::motion (GdkEvent *, bool)
3997 /* Total change in x and y since the start of the drag */
3998 frameoffset_t const dx = total_dx ();
3999 int8_t const dy = total_dy ();
4001 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4002 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4003 double const tdy = -dy * _note_height - _cumulative_dy;
4006 _cumulative_dx += tdx;
4007 _cumulative_dy += tdy;
4009 int8_t note_delta = total_dy();
4011 _region->move_selection (tdx, tdy, note_delta);
4013 /* the new note value may be the same as the old one, but we
4014 * don't know what that means because the selection may have
4015 * involved more than one note and we might be doing something
4016 * odd with them. so show the note value anyway, always.
4020 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4022 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4023 (int) floor (new_note));
4025 show_verbose_cursor_text (buf);
4030 NoteDrag::finished (GdkEvent* ev, bool moved)
4033 if (_editor->current_mouse_mode() == Editing::MouseObject) {
4035 if (_was_selected) {
4036 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4038 _region->note_deselected (_primary);
4041 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4042 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4044 if (!extend && !add && _region->selection_size() > 1) {
4045 _region->unique_select (_primary);
4046 } else if (extend) {
4047 _region->note_selected (_primary, true, true);
4049 /* it was added during button press */
4054 _region->note_dropped (_primary, total_dx(), total_dy());
4059 NoteDrag::aborted (bool)
4064 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
4065 : Drag (editor, item)
4067 , _nothing_to_drag (false)
4069 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4071 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
4074 /* get all lines in the automation view */
4075 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
4077 /* find those that overlap the ranges being dragged */
4078 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
4079 while (i != lines.end ()) {
4080 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4083 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4085 /* check this range against all the AudioRanges that we are using */
4086 list<AudioRange>::const_iterator k = _ranges.begin ();
4087 while (k != _ranges.end()) {
4088 if (k->coverage (r.first, r.second) != OverlapNone) {
4094 /* add it to our list if it overlaps at all */
4095 if (k != _ranges.end()) {
4100 _lines.push_back (n);
4106 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4110 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4112 Drag::start_grab (event, cursor);
4114 /* Get line states before we start changing things */
4115 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4116 i->state = &i->line->get_state ();
4119 if (_ranges.empty()) {
4121 /* No selected time ranges: drag all points */
4122 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4123 uint32_t const N = i->line->npoints ();
4124 for (uint32_t j = 0; j < N; ++j) {
4125 i->points.push_back (i->line->nth (j));
4131 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4133 framecnt_t const half = (i->start + i->end) / 2;
4135 /* find the line that this audio range starts in */
4136 list<Line>::iterator j = _lines.begin();
4137 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4141 if (j != _lines.end()) {
4142 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4144 /* j is the line that this audio range starts in; fade into it;
4145 64 samples length plucked out of thin air.
4148 framepos_t a = i->start + 64;
4153 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4154 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4156 the_list->add (p, the_list->eval (p));
4157 j->line->add_always_in_view (p);
4158 the_list->add (q, the_list->eval (q));
4159 j->line->add_always_in_view (q);
4162 /* same thing for the end */
4165 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4169 if (j != _lines.end()) {
4170 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4172 /* j is the line that this audio range starts in; fade out of it;
4173 64 samples length plucked out of thin air.
4176 framepos_t b = i->end - 64;
4181 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4182 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4184 the_list->add (p, the_list->eval (p));
4185 j->line->add_always_in_view (p);
4186 the_list->add (q, the_list->eval (q));
4187 j->line->add_always_in_view (q);
4191 _nothing_to_drag = true;
4193 /* Find all the points that should be dragged and put them in the relevant
4194 points lists in the Line structs.
4197 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4199 uint32_t const N = i->line->npoints ();
4200 for (uint32_t j = 0; j < N; ++j) {
4202 /* here's a control point on this line */
4203 ControlPoint* p = i->line->nth (j);
4204 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4206 /* see if it's inside a range */
4207 list<AudioRange>::const_iterator k = _ranges.begin ();
4208 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4212 if (k != _ranges.end()) {
4213 /* dragging this point */
4214 _nothing_to_drag = false;
4215 i->points.push_back (p);
4221 if (_nothing_to_drag) {
4225 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4226 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4231 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4233 if (_nothing_to_drag) {
4237 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4238 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4240 /* we are ignoring x position for this drag, so we can just pass in anything */
4241 i->line->drag_motion (0, f, true, false);
4246 AutomationRangeDrag::finished (GdkEvent* event, bool)
4248 if (_nothing_to_drag) {
4252 motion (event, false);
4253 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4254 i->line->end_drag ();
4255 i->line->clear_always_in_view ();
4258 _editor->session()->commit_reversible_command ();
4262 AutomationRangeDrag::aborted (bool)
4264 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4265 i->line->clear_always_in_view ();
4270 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4273 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4274 layer = v->region()->layer ();
4275 initial_y = v->get_canvas_group()->property_y ();
4276 initial_playlist = v->region()->playlist ();
4277 initial_position = v->region()->position ();
4278 initial_end = v->region()->position () + v->region()->length ();
4281 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4285 , _cumulative_dx (0)
4287 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4291 PatchChangeDrag::motion (GdkEvent* ev, bool)
4293 framepos_t f = adjusted_current_frame (ev);
4294 boost::shared_ptr<Region> r = _region_view->region ();
4295 f = max (f, r->position ());
4296 f = min (f, r->last_frame ());
4298 framecnt_t const dxf = f - grab_frame();
4299 double const dxu = _editor->frame_to_unit (dxf);
4300 _patch_change->move (dxu - _cumulative_dx, 0);
4301 _cumulative_dx = dxu;
4305 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4307 if (!movement_occurred) {
4311 boost::shared_ptr<Region> r (_region_view->region ());
4313 framepos_t f = adjusted_current_frame (ev);
4314 f = max (f, r->position ());
4315 f = min (f, r->last_frame ());
4317 _region_view->move_patch_change (
4319 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4324 PatchChangeDrag::aborted (bool)
4326 _patch_change->move (-_cumulative_dx, 0);
4330 PatchChangeDrag::setup_pointer_frame_offset ()
4332 boost::shared_ptr<Region> region = _region_view->region ();
4333 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4336 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4337 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4344 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4346 framepos_t const p = _region_view->region()->position ();
4347 double const y = _region_view->midi_view()->y_position ();
4349 x1 = max ((framepos_t) 0, x1 - p);
4350 x2 = max ((framepos_t) 0, x2 - p);
4351 y1 = max (0.0, y1 - y);
4352 y2 = max (0.0, y2 - y);
4354 _region_view->update_drag_selection (
4355 _editor->frame_to_pixel (x1),
4356 _editor->frame_to_pixel (x2),
4359 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4364 MidiRubberbandSelectDrag::deselect_things ()
4369 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4370 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4373 _vertical_only = true;
4377 MidiVerticalSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4379 double const y = _region_view->midi_view()->y_position ();
4381 y1 = max (0.0, y1 - y);
4382 y2 = max (0.0, y2 - y);
4384 _region_view->update_vertical_drag_selection (
4387 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4392 MidiVerticalSelectDrag::deselect_things ()
4397 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4398 : RubberbandSelectDrag (e, i)
4404 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4406 if (drag_in_progress) {
4407 /* We just want to select things at the end of the drag, not during it */
4411 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4413 _editor->begin_reversible_command (_("rubberband selection"));
4414 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4415 _editor->commit_reversible_command ();
4419 EditorRubberbandSelectDrag::deselect_things ()
4421 if (!getenv("ARDOUR_SAE")) {
4422 _editor->selection->clear_tracks();
4424 _editor->selection->clear_regions();
4425 _editor->selection->clear_points ();
4426 _editor->selection->clear_lines ();
4429 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4437 NoteCreateDrag::~NoteCreateDrag ()
4443 NoteCreateDrag::grid_frames (framepos_t t) const
4446 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4451 return _region_view->region_beats_to_region_frames (grid_beats);
4455 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4457 Drag::start_grab (event, cursor);
4459 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4461 framepos_t pf = _drags->current_pointer_frame ();
4462 framecnt_t const g = grid_frames (pf);
4464 /* Hack so that we always snap to the note that we are over, instead of snapping
4465 to the next one if we're more than halfway through the one we're over.
4467 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4471 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4473 MidiStreamView* sv = _region_view->midi_stream_view ();
4474 double const x = _editor->frame_to_pixel (_note[0]);
4475 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4477 _drag_rect->property_x1() = x;
4478 _drag_rect->property_y1() = y;
4479 _drag_rect->property_x2() = x;
4480 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4482 _drag_rect->property_outline_what() = 0xff;
4483 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4484 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4488 NoteCreateDrag::motion (GdkEvent* event, bool)
4490 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4491 double const x = _editor->frame_to_pixel (_note[1]);
4492 if (_note[1] > _note[0]) {
4493 _drag_rect->property_x2() = x;
4495 _drag_rect->property_x1() = x;
4500 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4502 if (!had_movement) {
4506 framepos_t const start = min (_note[0], _note[1]);
4507 framecnt_t length = abs (_note[0] - _note[1]);
4509 framecnt_t const g = grid_frames (start);
4510 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4512 if (_editor->snap_mode() == SnapNormal && length < g) {
4513 length = g - one_tick;
4516 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4518 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4522 NoteCreateDrag::y_to_region (double y) const
4525 _region_view->get_canvas_group()->w2i (x, y);
4530 NoteCreateDrag::aborted (bool)