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 ();
901 if (_editor->session() && Config->get_always_play_range()) {
902 _editor->session()->request_locate (_editor->get_selection().regions.start());
907 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
909 RegionSelection new_views;
910 PlaylistSet modified_playlists;
911 list<RegionView*> views_to_delete;
914 /* all changes were made during motion event handlers */
916 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
920 _editor->commit_reversible_command ();
924 if (_x_constrained) {
925 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
927 _editor->begin_reversible_command (Operations::region_copy);
930 /* insert the regions into their new playlists */
931 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
933 if (i->view->region()->locked()) {
939 if (changed_position && !_x_constrained) {
940 where = i->view->region()->position() - drag_delta;
942 where = i->view->region()->position();
945 RegionView* new_view = insert_region_into_playlist (
946 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
953 new_views.push_back (new_view);
955 /* we don't need the copied RegionView any more */
956 views_to_delete.push_back (i->view);
959 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
960 because when views are deleted they are automagically removed from _views, which messes
963 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
967 /* If we've created new regions either by copying or moving
968 to a new track, we want to replace the old selection with the new ones
971 if (new_views.size() > 0) {
972 _editor->selection->set (new_views);
975 /* write commands for the accumulated diffs for all our modified playlists */
976 add_stateful_diff_commands_for_playlists (modified_playlists);
978 _editor->commit_reversible_command ();
982 RegionMoveDrag::finished_no_copy (
983 bool const changed_position,
984 bool const changed_tracks,
985 framecnt_t const drag_delta
988 RegionSelection new_views;
989 PlaylistSet modified_playlists;
990 PlaylistSet frozen_playlists;
991 set<RouteTimeAxisView*> views_to_update;
994 /* all changes were made during motion event handlers */
995 _editor->commit_reversible_command ();
999 if (_x_constrained) {
1000 _editor->begin_reversible_command (_("fixed time region drag"));
1002 _editor->begin_reversible_command (Operations::region_drag);
1005 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1007 RegionView* rv = i->view;
1009 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1010 double const dest_layer = i->layer;
1012 if (rv->region()->locked()) {
1017 views_to_update.insert (dest_rtv);
1021 if (changed_position && !_x_constrained) {
1022 where = rv->region()->position() - drag_delta;
1024 where = rv->region()->position();
1027 if (changed_tracks) {
1029 /* insert into new playlist */
1031 RegionView* new_view = insert_region_into_playlist (
1032 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1035 if (new_view == 0) {
1040 new_views.push_back (new_view);
1042 /* remove from old playlist */
1044 /* the region that used to be in the old playlist is not
1045 moved to the new one - we use a copy of it. as a result,
1046 any existing editor for the region should no longer be
1049 rv->hide_region_editor();
1050 rv->fake_set_opaque (false);
1052 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1056 rv->region()->clear_changes ();
1059 motion on the same track. plonk the previously reparented region
1060 back to its original canvas group (its streamview).
1061 No need to do anything for copies as they are fake regions which will be deleted.
1064 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1065 rv->get_canvas_group()->property_y() = i->initial_y;
1066 rv->get_time_axis_view().reveal_dependent_views (*rv);
1068 /* just change the model */
1070 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1072 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1073 playlist->set_layer (rv->region(), dest_layer);
1076 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1078 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1081 playlist->freeze ();
1084 /* this movement may result in a crossfade being modified, so we need to get undo
1085 data from the playlist as well as the region.
1088 r = modified_playlists.insert (playlist);
1090 playlist->clear_changes ();
1093 rv->region()->set_position (where);
1095 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1098 if (changed_tracks) {
1100 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1101 was selected in all of them, then removing it from a playlist will have removed all
1102 trace of it from _views (i.e. there were N regions selected, we removed 1,
1103 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1104 corresponding regionview, and _views is now empty).
1106 This could have invalidated any and all iterators into _views.
1108 The heuristic we use here is: if the region selection is empty, break out of the loop
1109 here. if the region selection is not empty, then restart the loop because we know that
1110 we must have removed at least the region(view) we've just been working on as well as any
1111 that we processed on previous iterations.
1113 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1114 we can just iterate.
1118 if (_views.empty()) {
1129 /* If we've created new regions either by copying or moving
1130 to a new track, we want to replace the old selection with the new ones
1133 if (new_views.size() > 0) {
1134 _editor->selection->set (new_views);
1137 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1141 /* write commands for the accumulated diffs for all our modified playlists */
1142 add_stateful_diff_commands_for_playlists (modified_playlists);
1144 _editor->commit_reversible_command ();
1146 /* We have futzed with the layering of canvas items on our streamviews.
1147 If any region changed layer, this will have resulted in the stream
1148 views being asked to set up their region views, and all will be well.
1149 If not, we might now have badly-ordered region views. Ask the StreamViews
1150 involved to sort themselves out, just in case.
1153 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1154 (*i)->view()->playlist_layered ((*i)->track ());
1158 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1159 * @param region Region to remove.
1160 * @param playlist playlist To remove from.
1161 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1162 * that clear_changes () is only called once per playlist.
1165 RegionMoveDrag::remove_region_from_playlist (
1166 boost::shared_ptr<Region> region,
1167 boost::shared_ptr<Playlist> playlist,
1168 PlaylistSet& modified_playlists
1171 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1174 playlist->clear_changes ();
1177 playlist->remove_region (region);
1181 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1182 * clearing the playlist's diff history first if necessary.
1183 * @param region Region to insert.
1184 * @param dest_rtv Destination RouteTimeAxisView.
1185 * @param dest_layer Destination layer.
1186 * @param where Destination position.
1187 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1188 * that clear_changes () is only called once per playlist.
1189 * @return New RegionView, or 0 if no insert was performed.
1192 RegionMoveDrag::insert_region_into_playlist (
1193 boost::shared_ptr<Region> region,
1194 RouteTimeAxisView* dest_rtv,
1197 PlaylistSet& modified_playlists
1200 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1201 if (!dest_playlist) {
1205 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1206 _new_region_view = 0;
1207 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1209 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1210 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1212 dest_playlist->clear_changes ();
1215 dest_playlist->add_region (region, where);
1217 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1218 dest_playlist->set_layer (region, dest_layer);
1223 assert (_new_region_view);
1225 return _new_region_view;
1229 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1231 _new_region_view = rv;
1235 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1237 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1238 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1240 _editor->session()->add_command (c);
1249 RegionMoveDrag::aborted (bool movement_occurred)
1253 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1260 RegionMotionDrag::aborted (movement_occurred);
1265 RegionMotionDrag::aborted (bool)
1267 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1268 if ((*i)->view()->layer_display() == Expanded) {
1269 (*i)->view()->set_layer_display (Stacked);
1273 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1274 RegionView* rv = i->view;
1275 TimeAxisView* tv = &(rv->get_time_axis_view ());
1276 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1278 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1279 rv->get_canvas_group()->property_y() = 0;
1280 rv->get_time_axis_view().reveal_dependent_views (*rv);
1281 rv->fake_set_opaque (false);
1282 rv->move (-_total_x_delta, 0);
1283 rv->set_height (rtv->view()->child_height ());
1286 _editor->update_canvas_now ();
1289 /** @param b true to brush, otherwise false.
1290 * @param c true to make copies of the regions being moved, otherwise false.
1292 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1293 : RegionMotionDrag (e, i, p, v, b),
1296 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1299 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1300 if (rtv && rtv->is_track()) {
1301 speed = rtv->track()->speed ();
1304 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1308 RegionMoveDrag::setup_pointer_frame_offset ()
1310 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1313 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1314 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1316 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1318 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1319 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1321 _primary = v->view()->create_region_view (r, false, false);
1323 _primary->get_canvas_group()->show ();
1324 _primary->set_position (pos, 0);
1325 _views.push_back (DraggingView (_primary, this));
1327 _last_frame_position = pos;
1329 _item = _primary->get_canvas_group ();
1333 RegionInsertDrag::finished (GdkEvent *, bool)
1335 _editor->update_canvas_now ();
1337 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1339 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1340 _primary->get_canvas_group()->property_y() = 0;
1342 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1344 _editor->begin_reversible_command (Operations::insert_region);
1345 playlist->clear_changes ();
1346 playlist->add_region (_primary->region (), _last_frame_position);
1347 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1348 _editor->commit_reversible_command ();
1356 RegionInsertDrag::aborted (bool)
1363 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1364 : RegionMoveDrag (e, i, p, v, false, false)
1366 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1369 struct RegionSelectionByPosition {
1370 bool operator() (RegionView*a, RegionView* b) {
1371 return a->region()->position () < b->region()->position();
1376 RegionSpliceDrag::motion (GdkEvent* event, bool)
1378 /* Which trackview is this ? */
1380 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1381 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1383 /* The region motion is only processed if the pointer is over
1387 if (!tv || !tv->is_track()) {
1388 /* To make sure we hide the verbose canvas cursor when the mouse is
1389 not held over and audiotrack.
1391 _editor->verbose_cursor()->hide ();
1397 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1403 RegionSelection copy (_editor->selection->regions);
1405 RegionSelectionByPosition cmp;
1408 framepos_t const pf = adjusted_current_frame (event);
1410 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1412 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1418 boost::shared_ptr<Playlist> playlist;
1420 if ((playlist = atv->playlist()) == 0) {
1424 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1429 if (pf < (*i)->region()->last_frame() + 1) {
1433 if (pf > (*i)->region()->first_frame()) {
1439 playlist->shuffle ((*i)->region(), dir);
1444 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1446 RegionMoveDrag::finished (event, movement_occurred);
1450 RegionSpliceDrag::aborted (bool)
1455 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1457 _view (dynamic_cast<MidiTimeAxisView*> (v))
1459 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1465 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1468 _region = add_midi_region (_view);
1469 _view->playlist()->freeze ();
1472 framepos_t const f = adjusted_current_frame (event);
1473 if (f < grab_frame()) {
1474 _region->set_position (f);
1477 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1478 so that if this region is duplicated, its duplicate starts on
1479 a snap point rather than 1 frame after a snap point. Otherwise things get
1480 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1481 place snapped notes at the start of the region.
1484 framecnt_t const len = abs (f - grab_frame () - 1);
1485 _region->set_length (len < 1 ? 1 : len);
1491 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1493 if (!movement_occurred) {
1494 add_midi_region (_view);
1496 _view->playlist()->thaw ();
1500 _editor->commit_reversible_command ();
1505 RegionCreateDrag::aborted (bool)
1508 _view->playlist()->thaw ();
1514 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1518 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1522 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1524 Gdk::Cursor* cursor;
1525 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1526 float x_fraction = cnote->mouse_x_fraction ();
1528 if (x_fraction > 0.0 && x_fraction < 0.25) {
1529 cursor = _editor->cursors()->left_side_trim;
1531 cursor = _editor->cursors()->right_side_trim;
1534 Drag::start_grab (event, cursor);
1536 region = &cnote->region_view();
1538 double const region_start = region->get_position_pixels();
1539 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1541 if (grab_x() <= middle_point) {
1542 cursor = _editor->cursors()->left_side_trim;
1545 cursor = _editor->cursors()->right_side_trim;
1549 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1551 if (event->motion.state & Keyboard::PrimaryModifier) {
1557 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1559 if (ms.size() > 1) {
1560 /* has to be relative, may make no sense otherwise */
1564 /* select this note; if it is already selected, preserve the existing selection,
1565 otherwise make this note the only one selected.
1567 region->note_selected (cnote, cnote->selected ());
1569 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1570 MidiRegionSelection::iterator next;
1573 (*r)->begin_resizing (at_front);
1579 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1581 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1582 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1583 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1588 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1590 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1591 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1592 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1597 NoteResizeDrag::aborted (bool)
1599 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1600 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1601 (*r)->abort_resizing ();
1605 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1608 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1612 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1618 RegionGainDrag::finished (GdkEvent *, bool)
1624 RegionGainDrag::aborted (bool)
1629 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1630 : RegionDrag (e, i, p, v)
1632 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1636 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1639 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1640 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1642 if (tv && tv->is_track()) {
1643 speed = tv->track()->speed();
1646 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1647 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1648 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1650 framepos_t const pf = adjusted_current_frame (event);
1652 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1653 /* Move the contents of the region around without changing the region bounds */
1654 _operation = ContentsTrim;
1655 Drag::start_grab (event, _editor->cursors()->trimmer);
1657 /* These will get overridden for a point trim.*/
1658 if (pf < (region_start + region_length/2)) {
1659 /* closer to front */
1660 _operation = StartTrim;
1661 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1664 _operation = EndTrim;
1665 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1669 switch (_operation) {
1671 show_verbose_cursor_time (region_start);
1672 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1673 i->view->trim_front_starting ();
1677 show_verbose_cursor_time (region_end);
1680 show_verbose_cursor_time (pf);
1684 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1685 i->view->region()->suspend_property_changes ();
1690 TrimDrag::motion (GdkEvent* event, bool first_move)
1692 RegionView* rv = _primary;
1695 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1696 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1697 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1699 if (tv && tv->is_track()) {
1700 speed = tv->track()->speed();
1703 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1709 switch (_operation) {
1711 trim_type = "Region start trim";
1714 trim_type = "Region end trim";
1717 trim_type = "Region content trim";
1721 _editor->begin_reversible_command (trim_type);
1723 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1724 RegionView* rv = i->view;
1725 rv->fake_set_opaque (false);
1726 rv->enable_display (false);
1727 rv->region()->playlist()->clear_owned_changes ();
1729 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1732 arv->temporarily_hide_envelope ();
1735 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1736 insert_result = _editor->motion_frozen_playlists.insert (pl);
1738 if (insert_result.second) {
1744 bool non_overlap_trim = false;
1746 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1747 non_overlap_trim = true;
1750 switch (_operation) {
1752 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1753 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1758 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1759 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1765 bool swap_direction = false;
1767 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1768 swap_direction = true;
1771 framecnt_t frame_delta = 0;
1773 bool left_direction = false;
1774 if (last_pointer_frame() > adjusted_current_frame(event)) {
1775 left_direction = true;
1778 if (left_direction) {
1779 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1781 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1784 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1785 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1791 switch (_operation) {
1793 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1796 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1799 show_verbose_cursor_time (adjusted_current_frame (event));
1806 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1808 if (movement_occurred) {
1809 motion (event, false);
1811 /* This must happen before the region's StatefulDiffCommand is created, as it may
1812 `correct' (ahem) the region's _start from being negative to being zero. It
1813 needs to be zero in the undo record.
1815 if (_operation == StartTrim) {
1816 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1817 i->view->trim_front_ending ();
1821 if (!_editor->selection->selected (_primary)) {
1822 _primary->thaw_after_trim ();
1825 set<boost::shared_ptr<Playlist> > diffed_playlists;
1827 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1828 i->view->thaw_after_trim ();
1829 i->view->enable_display (true);
1830 i->view->fake_set_opaque (true);
1832 /* Trimming one region may affect others on the playlist, so we need
1833 to get undo Commands from the whole playlist rather than just the
1834 region. Use diffed_playlists to make sure we don't diff a given
1835 playlist more than once.
1837 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1838 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1839 vector<Command*> cmds;
1841 _editor->session()->add_commands (cmds);
1842 diffed_playlists.insert (p);
1846 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1850 _editor->motion_frozen_playlists.clear ();
1851 _editor->commit_reversible_command();
1854 /* no mouse movement */
1855 _editor->point_trim (event, adjusted_current_frame (event));
1858 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1859 if (_operation == StartTrim) {
1860 i->view->trim_front_ending ();
1863 i->view->region()->resume_property_changes ();
1868 TrimDrag::aborted (bool movement_occurred)
1870 /* Our motion method is changing model state, so use the Undo system
1871 to cancel. Perhaps not ideal, as this will leave an Undo point
1872 behind which may be slightly odd from the user's point of view.
1877 if (movement_occurred) {
1881 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1882 i->view->region()->resume_property_changes ();
1887 TrimDrag::setup_pointer_frame_offset ()
1889 list<DraggingView>::iterator i = _views.begin ();
1890 while (i != _views.end() && i->view != _primary) {
1894 if (i == _views.end()) {
1898 switch (_operation) {
1900 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1903 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1910 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1914 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1915 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1920 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1922 Drag::start_grab (event, cursor);
1923 show_verbose_cursor_time (adjusted_current_frame(event));
1927 MeterMarkerDrag::setup_pointer_frame_offset ()
1929 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1933 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1937 // create a dummy marker for visual representation of moving the
1938 // section, because whether its a copy or not, we're going to
1939 // leave or lose the original marker (leave if its a copy; lose if its
1940 // not, because we'll remove it from the map).
1942 MeterSection section (_marker->meter());
1944 if (!section.movable()) {
1949 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1951 _marker = new MeterMarker (
1953 *_editor->meter_group,
1954 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1956 *new MeterSection (_marker->meter())
1959 /* use the new marker for the grab */
1960 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1963 TempoMap& map (_editor->session()->tempo_map());
1964 /* get current state */
1965 before_state = &map.get_state();
1966 /* remove the section while we drag it */
1967 map.remove_meter (section, true);
1971 framepos_t const pf = adjusted_current_frame (event);
1972 _marker->set_position (pf);
1973 show_verbose_cursor_time (pf);
1977 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1979 if (!movement_occurred) {
1983 motion (event, false);
1985 Timecode::BBT_Time when;
1987 TempoMap& map (_editor->session()->tempo_map());
1988 map.bbt_time (last_pointer_frame(), when);
1990 if (_copy == true) {
1991 _editor->begin_reversible_command (_("copy meter mark"));
1992 XMLNode &before = map.get_state();
1993 map.add_meter (_marker->meter(), when);
1994 XMLNode &after = map.get_state();
1995 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1996 _editor->commit_reversible_command ();
1999 _editor->begin_reversible_command (_("move meter mark"));
2001 /* we removed it before, so add it back now */
2003 map.add_meter (_marker->meter(), when);
2004 XMLNode &after = map.get_state();
2005 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2006 _editor->commit_reversible_command ();
2009 // delete the dummy marker we used for visual representation while moving.
2010 // a new visual marker will show up automatically.
2015 MeterMarkerDrag::aborted (bool moved)
2017 _marker->set_position (_marker->meter().frame ());
2020 TempoMap& map (_editor->session()->tempo_map());
2021 /* we removed it before, so add it back now */
2022 map.add_meter (_marker->meter(), _marker->meter().frame());
2023 // delete the dummy marker we used for visual representation while moving.
2024 // a new visual marker will show up automatically.
2029 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2033 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2035 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2040 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2042 Drag::start_grab (event, cursor);
2043 show_verbose_cursor_time (adjusted_current_frame (event));
2047 TempoMarkerDrag::setup_pointer_frame_offset ()
2049 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2053 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2057 // create a dummy marker for visual representation of moving the
2058 // section, because whether its a copy or not, we're going to
2059 // leave or lose the original marker (leave if its a copy; lose if its
2060 // not, because we'll remove it from the map).
2062 // create a dummy marker for visual representation of moving the copy.
2063 // The actual copying is not done before we reach the finish callback.
2066 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2068 TempoSection section (_marker->tempo());
2070 _marker = new TempoMarker (
2072 *_editor->tempo_group,
2073 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2075 *new TempoSection (_marker->tempo())
2078 /* use the new marker for the grab */
2079 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2082 TempoMap& map (_editor->session()->tempo_map());
2083 /* get current state */
2084 before_state = &map.get_state();
2085 /* remove the section while we drag it */
2086 map.remove_tempo (section, true);
2090 framepos_t const pf = adjusted_current_frame (event);
2091 _marker->set_position (pf);
2092 show_verbose_cursor_time (pf);
2096 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2098 if (!movement_occurred) {
2102 motion (event, false);
2104 TempoMap& map (_editor->session()->tempo_map());
2105 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2106 Timecode::BBT_Time when;
2108 map.bbt_time (beat_time, when);
2110 if (_copy == true) {
2111 _editor->begin_reversible_command (_("copy tempo mark"));
2112 XMLNode &before = map.get_state();
2113 map.add_tempo (_marker->tempo(), when);
2114 XMLNode &after = map.get_state();
2115 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2116 _editor->commit_reversible_command ();
2119 _editor->begin_reversible_command (_("move tempo mark"));
2120 /* we removed it before, so add it back now */
2121 map.add_tempo (_marker->tempo(), when);
2122 XMLNode &after = map.get_state();
2123 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2124 _editor->commit_reversible_command ();
2127 // delete the dummy marker we used for visual representation while moving.
2128 // a new visual marker will show up automatically.
2133 TempoMarkerDrag::aborted (bool moved)
2135 _marker->set_position (_marker->tempo().frame());
2137 TempoMap& map (_editor->session()->tempo_map());
2138 /* we removed it before, so add it back now */
2139 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2140 // delete the dummy marker we used for visual representation while moving.
2141 // a new visual marker will show up automatically.
2146 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2150 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2153 /** Do all the things we do when dragging the playhead to make it look as though
2154 * we have located, without actually doing the locate (because that would cause
2155 * the diskstream buffers to be refilled, which is too slow).
2158 CursorDrag::fake_locate (framepos_t t)
2160 _editor->playhead_cursor->set_position (t);
2162 Session* s = _editor->session ();
2163 if (s->timecode_transmission_suspended ()) {
2164 framepos_t const f = _editor->playhead_cursor->current_frame;
2165 s->send_mmc_locate (f);
2166 s->send_full_time_code (f);
2169 show_verbose_cursor_time (t);
2170 _editor->UpdateAllTransportClocks (t);
2174 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2176 Drag::start_grab (event, c);
2178 _grab_zoom = _editor->frames_per_unit;
2180 framepos_t where = _editor->event_frame (event, 0, 0);
2181 _editor->snap_to_with_modifier (where, event);
2183 _editor->_dragging_playhead = true;
2185 Session* s = _editor->session ();
2188 if (_was_rolling && _stop) {
2192 if (s->is_auditioning()) {
2193 s->cancel_audition ();
2196 s->request_suspend_timecode_transmission ();
2197 while (!s->timecode_transmission_suspended ()) {
2198 /* twiddle our thumbs */
2202 fake_locate (where);
2206 CursorDrag::motion (GdkEvent* event, bool)
2208 framepos_t const adjusted_frame = adjusted_current_frame (event);
2209 if (adjusted_frame != last_pointer_frame()) {
2210 fake_locate (adjusted_frame);
2212 _editor->update_canvas_now ();
2218 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2220 _editor->_dragging_playhead = false;
2222 if (!movement_occurred && _stop) {
2226 motion (event, false);
2228 Session* s = _editor->session ();
2230 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2231 _editor->_pending_locate_request = true;
2232 s->request_resume_timecode_transmission ();
2237 CursorDrag::aborted (bool)
2239 if (_editor->_dragging_playhead) {
2240 _editor->session()->request_resume_timecode_transmission ();
2241 _editor->_dragging_playhead = false;
2244 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2247 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2248 : RegionDrag (e, i, p, v)
2250 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2254 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2256 Drag::start_grab (event, cursor);
2258 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2259 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2261 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2263 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2267 FadeInDrag::setup_pointer_frame_offset ()
2269 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2270 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2271 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2275 FadeInDrag::motion (GdkEvent* event, bool)
2277 framecnt_t fade_length;
2279 framepos_t const pos = adjusted_current_frame (event);
2281 boost::shared_ptr<Region> region = _primary->region ();
2283 if (pos < (region->position() + 64)) {
2284 fade_length = 64; // this should be a minimum defined somewhere
2285 } else if (pos > region->last_frame()) {
2286 fade_length = region->length();
2288 fade_length = pos - region->position();
2291 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2293 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2299 tmp->reset_fade_in_shape_width (fade_length);
2300 tmp->show_fade_line((framecnt_t) fade_length);
2303 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2307 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2309 if (!movement_occurred) {
2313 framecnt_t fade_length;
2315 framepos_t const pos = adjusted_current_frame (event);
2317 boost::shared_ptr<Region> region = _primary->region ();
2319 if (pos < (region->position() + 64)) {
2320 fade_length = 64; // this should be a minimum defined somewhere
2321 } else if (pos > region->last_frame()) {
2322 fade_length = region->length();
2324 fade_length = pos - region->position();
2327 _editor->begin_reversible_command (_("change fade in length"));
2329 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2331 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2337 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2338 XMLNode &before = alist->get_state();
2340 tmp->audio_region()->set_fade_in_length (fade_length);
2341 tmp->audio_region()->set_fade_in_active (true);
2342 tmp->hide_fade_line();
2344 XMLNode &after = alist->get_state();
2345 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2348 _editor->commit_reversible_command ();
2352 FadeInDrag::aborted (bool)
2354 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2355 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2361 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2362 tmp->hide_fade_line();
2366 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2367 : RegionDrag (e, i, p, v)
2369 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2373 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2375 Drag::start_grab (event, cursor);
2377 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2378 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2380 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2382 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2386 FadeOutDrag::setup_pointer_frame_offset ()
2388 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2389 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2390 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2394 FadeOutDrag::motion (GdkEvent* event, bool)
2396 framecnt_t fade_length;
2398 framepos_t const pos = adjusted_current_frame (event);
2400 boost::shared_ptr<Region> region = _primary->region ();
2402 if (pos > (region->last_frame() - 64)) {
2403 fade_length = 64; // this should really be a minimum fade defined somewhere
2405 else if (pos < region->position()) {
2406 fade_length = region->length();
2409 fade_length = region->last_frame() - pos;
2412 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2414 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2420 tmp->reset_fade_out_shape_width (fade_length);
2421 tmp->show_fade_line(region->length() - fade_length);
2424 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2428 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2430 if (!movement_occurred) {
2434 framecnt_t fade_length;
2436 framepos_t const pos = adjusted_current_frame (event);
2438 boost::shared_ptr<Region> region = _primary->region ();
2440 if (pos > (region->last_frame() - 64)) {
2441 fade_length = 64; // this should really be a minimum fade defined somewhere
2443 else if (pos < region->position()) {
2444 fade_length = region->length();
2447 fade_length = region->last_frame() - pos;
2450 _editor->begin_reversible_command (_("change fade out length"));
2452 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2454 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2460 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2461 XMLNode &before = alist->get_state();
2463 tmp->audio_region()->set_fade_out_length (fade_length);
2464 tmp->audio_region()->set_fade_out_active (true);
2465 tmp->hide_fade_line();
2467 XMLNode &after = alist->get_state();
2468 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2471 _editor->commit_reversible_command ();
2475 FadeOutDrag::aborted (bool)
2477 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2478 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2484 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2485 tmp->hide_fade_line();
2489 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2492 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2494 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2497 _points.push_back (Gnome::Art::Point (0, 0));
2498 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2501 MarkerDrag::~MarkerDrag ()
2503 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2509 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2511 Drag::start_grab (event, cursor);
2515 Location *location = _editor->find_location_from_marker (_marker, is_start);
2516 _editor->_dragging_edit_point = true;
2518 update_item (location);
2520 // _drag_line->show();
2521 // _line->raise_to_top();
2524 show_verbose_cursor_time (location->start());
2526 show_verbose_cursor_time (location->end());
2529 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2532 case Selection::Toggle:
2533 _editor->selection->toggle (_marker);
2535 case Selection::Set:
2536 if (!_editor->selection->selected (_marker)) {
2537 _editor->selection->set (_marker);
2540 case Selection::Extend:
2542 Locations::LocationList ll;
2543 list<Marker*> to_add;
2545 _editor->selection->markers.range (s, e);
2546 s = min (_marker->position(), s);
2547 e = max (_marker->position(), e);
2550 if (e < max_framepos) {
2553 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2554 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2555 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2558 to_add.push_back (lm->start);
2561 to_add.push_back (lm->end);
2565 if (!to_add.empty()) {
2566 _editor->selection->add (to_add);
2570 case Selection::Add:
2571 _editor->selection->add (_marker);
2575 /* Set up copies for us to manipulate during the drag */
2577 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2578 Location* l = _editor->find_location_from_marker (*i, is_start);
2579 _copied_locations.push_back (new Location (*l));
2584 MarkerDrag::setup_pointer_frame_offset ()
2587 Location *location = _editor->find_location_from_marker (_marker, is_start);
2588 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2592 MarkerDrag::motion (GdkEvent* event, bool)
2594 framecnt_t f_delta = 0;
2596 bool move_both = false;
2598 Location *real_location;
2599 Location *copy_location = 0;
2601 framepos_t const newframe = adjusted_current_frame (event);
2603 framepos_t next = newframe;
2605 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2609 MarkerSelection::iterator i;
2610 list<Location*>::iterator x;
2612 /* find the marker we're dragging, and compute the delta */
2614 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2615 x != _copied_locations.end() && i != _editor->selection->markers.end();
2621 if (marker == _marker) {
2623 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2628 if (real_location->is_mark()) {
2629 f_delta = newframe - copy_location->start();
2633 switch (marker->type()) {
2634 case Marker::SessionStart:
2635 case Marker::RangeStart:
2636 case Marker::LoopStart:
2637 case Marker::PunchIn:
2638 f_delta = newframe - copy_location->start();
2641 case Marker::SessionEnd:
2642 case Marker::RangeEnd:
2643 case Marker::LoopEnd:
2644 case Marker::PunchOut:
2645 f_delta = newframe - copy_location->end();
2648 /* what kind of marker is this ? */
2656 if (i == _editor->selection->markers.end()) {
2657 /* hmm, impossible - we didn't find the dragged marker */
2661 /* now move them all */
2663 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2664 x != _copied_locations.end() && i != _editor->selection->markers.end();
2670 /* call this to find out if its the start or end */
2672 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2676 if (real_location->locked()) {
2680 if (copy_location->is_mark()) {
2684 copy_location->set_start (copy_location->start() + f_delta);
2688 framepos_t new_start = copy_location->start() + f_delta;
2689 framepos_t new_end = copy_location->end() + f_delta;
2691 if (is_start) { // start-of-range marker
2694 copy_location->set_start (new_start);
2695 copy_location->set_end (new_end);
2696 } else if (new_start < copy_location->end()) {
2697 copy_location->set_start (new_start);
2698 } else if (newframe > 0) {
2699 _editor->snap_to (next, 1, true);
2700 copy_location->set_end (next);
2701 copy_location->set_start (newframe);
2704 } else { // end marker
2707 copy_location->set_end (new_end);
2708 copy_location->set_start (new_start);
2709 } else if (new_end > copy_location->start()) {
2710 copy_location->set_end (new_end);
2711 } else if (newframe > 0) {
2712 _editor->snap_to (next, -1, true);
2713 copy_location->set_start (next);
2714 copy_location->set_end (newframe);
2719 update_item (copy_location);
2721 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2724 lm->set_position (copy_location->start(), copy_location->end());
2728 assert (!_copied_locations.empty());
2730 show_verbose_cursor_time (newframe);
2733 _editor->update_canvas_now ();
2738 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2740 if (!movement_occurred) {
2742 /* just a click, do nothing but finish
2743 off the selection process
2746 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2749 case Selection::Set:
2750 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2751 _editor->selection->set (_marker);
2755 case Selection::Toggle:
2756 case Selection::Extend:
2757 case Selection::Add:
2764 _editor->_dragging_edit_point = false;
2766 _editor->begin_reversible_command ( _("move marker") );
2767 XMLNode &before = _editor->session()->locations()->get_state();
2769 MarkerSelection::iterator i;
2770 list<Location*>::iterator x;
2773 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2774 x != _copied_locations.end() && i != _editor->selection->markers.end();
2777 Location * location = _editor->find_location_from_marker (*i, is_start);
2781 if (location->locked()) {
2785 if (location->is_mark()) {
2786 location->set_start ((*x)->start());
2788 location->set ((*x)->start(), (*x)->end());
2793 XMLNode &after = _editor->session()->locations()->get_state();
2794 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2795 _editor->commit_reversible_command ();
2799 MarkerDrag::aborted (bool)
2805 MarkerDrag::update_item (Location*)
2810 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2812 _cumulative_x_drag (0),
2813 _cumulative_y_drag (0)
2815 if (_zero_gain_fraction < 0.0) {
2816 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2819 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2821 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2827 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2829 Drag::start_grab (event, _editor->cursors()->fader);
2831 // start the grab at the center of the control point so
2832 // the point doesn't 'jump' to the mouse after the first drag
2833 _fixed_grab_x = _point->get_x();
2834 _fixed_grab_y = _point->get_y();
2836 float const fraction = 1 - (_point->get_y() / _point->line().height());
2838 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2840 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2841 event->button.x + 10, event->button.y + 10);
2843 _editor->verbose_cursor()->show ();
2847 ControlPointDrag::motion (GdkEvent* event, bool)
2849 double dx = _drags->current_pointer_x() - last_pointer_x();
2850 double dy = _drags->current_pointer_y() - last_pointer_y();
2852 if (event->button.state & Keyboard::SecondaryModifier) {
2857 /* coordinate in pixels relative to the start of the region (for region-based automation)
2858 or track (for track-based automation) */
2859 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2860 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2862 // calculate zero crossing point. back off by .01 to stay on the
2863 // positive side of zero
2864 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2866 // make sure we hit zero when passing through
2867 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2871 if (_x_constrained) {
2874 if (_y_constrained) {
2878 _cumulative_x_drag = cx - _fixed_grab_x;
2879 _cumulative_y_drag = cy - _fixed_grab_y;
2883 cy = min ((double) _point->line().height(), cy);
2885 framepos_t cx_frames = _editor->unit_to_frame (cx);
2887 if (!_x_constrained) {
2888 _editor->snap_to_with_modifier (cx_frames, event);
2891 cx_frames = min (cx_frames, _point->line().maximum_time());
2893 float const fraction = 1.0 - (cy / _point->line().height());
2895 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2897 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2899 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2903 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2905 if (!movement_occurred) {
2909 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2910 _editor->reset_point_selection ();
2914 motion (event, false);
2917 _point->line().end_drag ();
2918 _editor->session()->commit_reversible_command ();
2922 ControlPointDrag::aborted (bool)
2924 _point->line().reset ();
2928 ControlPointDrag::active (Editing::MouseMode m)
2930 if (m == Editing::MouseGain) {
2931 /* always active in mouse gain */
2935 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2936 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2939 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2942 _cumulative_y_drag (0)
2944 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2948 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2950 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2953 _item = &_line->grab_item ();
2955 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2956 origin, and ditto for y.
2959 double cx = event->button.x;
2960 double cy = event->button.y;
2962 _line->parent_group().w2i (cx, cy);
2964 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2969 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2970 /* no adjacent points */
2974 Drag::start_grab (event, _editor->cursors()->fader);
2976 /* store grab start in parent frame */
2981 double fraction = 1.0 - (cy / _line->height());
2983 _line->start_drag_line (before, after, fraction);
2985 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2986 event->button.x + 10, event->button.y + 10);
2988 _editor->verbose_cursor()->show ();
2992 LineDrag::motion (GdkEvent* event, bool)
2994 double dy = _drags->current_pointer_y() - last_pointer_y();
2996 if (event->button.state & Keyboard::SecondaryModifier) {
3000 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3002 _cumulative_y_drag = cy - _fixed_grab_y;
3005 cy = min ((double) _line->height(), cy);
3007 double const fraction = 1.0 - (cy / _line->height());
3011 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3017 /* we are ignoring x position for this drag, so we can just pass in anything */
3018 _line->drag_motion (0, fraction, true, push);
3020 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3024 LineDrag::finished (GdkEvent* event, bool)
3026 motion (event, false);
3028 _editor->session()->commit_reversible_command ();
3032 LineDrag::aborted (bool)
3037 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3040 _cumulative_x_drag (0)
3042 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3046 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3048 Drag::start_grab (event);
3050 _line = reinterpret_cast<Line*> (_item);
3053 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3055 double cx = event->button.x;
3056 double cy = event->button.y;
3058 _item->property_parent().get_value()->w2i(cx, cy);
3060 /* store grab start in parent frame */
3061 _region_view_grab_x = cx;
3063 _before = *(float*) _item->get_data ("position");
3065 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3067 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3071 FeatureLineDrag::motion (GdkEvent*, bool)
3073 double dx = _drags->current_pointer_x() - last_pointer_x();
3075 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3077 _cumulative_x_drag += dx;
3079 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3088 ArdourCanvas::Points points;
3090 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3092 _line->get_bounds(x1, y2, x2, y2);
3094 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3095 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3097 _line->property_points() = points;
3099 float *pos = new float;
3102 _line->set_data ("position", pos);
3108 FeatureLineDrag::finished (GdkEvent*, bool)
3110 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3111 _arv->update_transient(_before, _before);
3115 FeatureLineDrag::aborted (bool)
3120 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3122 , _vertical_only (false)
3124 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3128 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3130 Drag::start_grab (event);
3131 show_verbose_cursor_time (adjusted_current_frame (event));
3135 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3142 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3144 framepos_t grab = grab_frame ();
3145 if (Config->get_rubberbanding_snaps_to_grid ()) {
3146 _editor->snap_to_with_modifier (grab, event);
3149 /* base start and end on initial click position */
3159 if (_drags->current_pointer_y() < grab_y()) {
3160 y1 = _drags->current_pointer_y();
3163 y2 = _drags->current_pointer_y();
3168 if (start != end || y1 != y2) {
3170 double x1 = _editor->frame_to_pixel (start);
3171 double x2 = _editor->frame_to_pixel (end);
3173 _editor->rubberband_rect->property_x1() = x1;
3174 if (_vertical_only) {
3175 /* fixed 10 pixel width */
3176 _editor->rubberband_rect->property_x2() = x1 + 10;
3178 _editor->rubberband_rect->property_x2() = x2;
3181 _editor->rubberband_rect->property_y1() = y1;
3182 _editor->rubberband_rect->property_y2() = y2;
3184 _editor->rubberband_rect->show();
3185 _editor->rubberband_rect->raise_to_top();
3187 show_verbose_cursor_time (pf);
3189 do_select_things (event, true);
3194 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3199 if (grab_frame() < last_pointer_frame()) {
3201 x2 = last_pointer_frame ();
3204 x1 = last_pointer_frame ();
3210 if (_drags->current_pointer_y() < grab_y()) {
3211 y1 = _drags->current_pointer_y();
3214 y2 = _drags->current_pointer_y();
3218 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3222 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3224 if (movement_occurred) {
3226 motion (event, false);
3227 do_select_things (event, false);
3233 bool do_deselect = true;
3234 MidiTimeAxisView* mtv;
3236 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3238 if (_editor->selection->empty()) {
3239 /* nothing selected */
3240 add_midi_region (mtv);
3241 do_deselect = false;
3251 _editor->rubberband_rect->hide();
3255 RubberbandSelectDrag::aborted (bool)
3257 _editor->rubberband_rect->hide ();
3260 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3261 : RegionDrag (e, i, p, v)
3263 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3267 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3269 Drag::start_grab (event, cursor);
3271 show_verbose_cursor_time (adjusted_current_frame (event));
3275 TimeFXDrag::motion (GdkEvent* event, bool)
3277 RegionView* rv = _primary;
3279 framepos_t const pf = adjusted_current_frame (event);
3281 if (pf > rv->region()->position()) {
3282 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3285 show_verbose_cursor_time (pf);
3289 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3291 _primary->get_time_axis_view().hide_timestretch ();
3293 if (!movement_occurred) {
3297 if (last_pointer_frame() < _primary->region()->position()) {
3298 /* backwards drag of the left edge - not usable */
3302 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3304 float percentage = (double) newlen / (double) _primary->region()->length();
3306 #ifndef USE_RUBBERBAND
3307 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3308 if (_primary->region()->data_type() == DataType::AUDIO) {
3309 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3313 if (!_editor->get_selection().regions.empty()) {
3314 /* primary will already be included in the selection, and edit
3315 group shared editing will propagate selection across
3316 equivalent regions, so just use the current region
3320 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3321 error << _("An error occurred while executing time stretch operation") << endmsg;
3327 TimeFXDrag::aborted (bool)
3329 _primary->get_time_axis_view().hide_timestretch ();
3332 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3335 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3339 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3341 Drag::start_grab (event);
3345 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3347 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3351 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3353 if (movement_occurred && _editor->session()) {
3354 /* make sure we stop */
3355 _editor->session()->request_transport_speed (0.0);
3360 ScrubDrag::aborted (bool)
3365 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3369 , _original_pointer_time_axis (-1)
3370 , _last_pointer_time_axis (-1)
3371 , _time_selection_at_start (!_editor->get_selection().time.empty())
3373 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3377 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3379 if (_editor->session() == 0) {
3383 Gdk::Cursor* cursor = 0;
3385 switch (_operation) {
3386 case CreateSelection:
3387 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3392 cursor = _editor->cursors()->selector;
3393 Drag::start_grab (event, cursor);
3396 case SelectionStartTrim:
3397 if (_editor->clicked_axisview) {
3398 _editor->clicked_axisview->order_selection_trims (_item, true);
3400 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3403 case SelectionEndTrim:
3404 if (_editor->clicked_axisview) {
3405 _editor->clicked_axisview->order_selection_trims (_item, false);
3407 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3411 Drag::start_grab (event, cursor);
3415 if (_operation == SelectionMove) {
3416 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3418 show_verbose_cursor_time (adjusted_current_frame (event));
3421 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3425 SelectionDrag::setup_pointer_frame_offset ()
3427 switch (_operation) {
3428 case CreateSelection:
3429 _pointer_frame_offset = 0;
3432 case SelectionStartTrim:
3434 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3437 case SelectionEndTrim:
3438 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3444 SelectionDrag::motion (GdkEvent* event, bool first_move)
3446 framepos_t start = 0;
3450 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3451 if (pending_time_axis.first == 0) {
3455 framepos_t const pending_position = adjusted_current_frame (event);
3457 /* only alter selection if things have changed */
3459 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3463 switch (_operation) {
3464 case CreateSelection:
3466 framepos_t grab = grab_frame ();
3469 _editor->snap_to (grab);
3472 if (pending_position < grab_frame()) {
3473 start = pending_position;
3476 end = pending_position;
3480 /* first drag: Either add to the selection
3481 or create a new selection
3487 /* adding to the selection */
3488 _editor->set_selected_track_as_side_effect (Selection::Add);
3489 //_editor->selection->add (_editor->clicked_axisview);
3490 _editor->clicked_selection = _editor->selection->add (start, end);
3495 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3496 //_editor->selection->set (_editor->clicked_axisview);
3497 _editor->set_selected_track_as_side_effect (Selection::Set);
3500 _editor->clicked_selection = _editor->selection->set (start, end);
3504 /* select the track that we're in */
3505 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3506 // _editor->set_selected_track_as_side_effect (Selection::Add);
3507 _editor->selection->add (pending_time_axis.first);
3508 _added_time_axes.push_back (pending_time_axis.first);
3511 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3512 tracks that we selected in the first place.
3515 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3516 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3518 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3519 while (i != _added_time_axes.end()) {
3521 list<TimeAxisView*>::iterator tmp = i;
3524 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3525 _editor->selection->remove (*i);
3526 _added_time_axes.remove (*i);
3535 case SelectionStartTrim:
3537 start = _editor->selection->time[_editor->clicked_selection].start;
3538 end = _editor->selection->time[_editor->clicked_selection].end;
3540 if (pending_position > end) {
3543 start = pending_position;
3547 case SelectionEndTrim:
3549 start = _editor->selection->time[_editor->clicked_selection].start;
3550 end = _editor->selection->time[_editor->clicked_selection].end;
3552 if (pending_position < start) {
3555 end = pending_position;
3562 start = _editor->selection->time[_editor->clicked_selection].start;
3563 end = _editor->selection->time[_editor->clicked_selection].end;
3565 length = end - start;
3567 start = pending_position;
3568 _editor->snap_to (start);
3570 end = start + length;
3575 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3576 _editor->start_canvas_autoscroll (1, 0);
3580 _editor->selection->replace (_editor->clicked_selection, start, end);
3583 if (_operation == SelectionMove) {
3584 show_verbose_cursor_time(start);
3586 show_verbose_cursor_time(pending_position);
3591 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3593 Session* s = _editor->session();
3595 if (movement_occurred) {
3596 motion (event, false);
3597 /* XXX this is not object-oriented programming at all. ick */
3598 if (_editor->selection->time.consolidate()) {
3599 _editor->selection->TimeChanged ();
3602 /* XXX what if its a music time selection? */
3604 if ((s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3605 s->request_play_range (&_editor->selection->time, true);
3607 if (Config->get_always_play_range()) {
3608 if (_editor->doing_range_stuff()) {
3609 s->request_locate (_editor->get_selection().time.start());
3616 /* just a click, no pointer movement.
3619 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3620 if (!_time_selection_at_start) {
3621 if (_editor->clicked_regionview) {
3622 if (_editor->get_selection().selected (_editor->clicked_regionview)) {
3623 /* range select the entire current
3626 _editor->select_range (_editor->get_selection().regions.start(),
3627 _editor->get_selection().regions.end_frame());
3629 /* range select this (unselected)
3632 _editor->select_range (_editor->clicked_regionview->region()->position(),
3633 _editor->clicked_regionview->region()->last_frame());
3637 _editor->selection->clear_time();
3641 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3642 _editor->selection->set (_editor->clicked_axisview);
3645 if (s && s->get_play_range () && s->transport_rolling()) {
3646 s->request_stop (false, false);
3649 if (Config->get_always_play_range()) {
3650 if (_editor->doing_range_stuff()) {
3651 s->request_locate (_editor->get_selection().time.start());
3656 _editor->stop_canvas_autoscroll ();
3660 SelectionDrag::aborted (bool)
3665 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3670 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3672 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3673 physical_screen_height (_editor->get_window()));
3674 _drag_rect->hide ();
3676 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3677 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3681 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3683 if (_editor->session() == 0) {
3687 Gdk::Cursor* cursor = 0;
3689 if (!_editor->temp_location) {
3690 _editor->temp_location = new Location (*_editor->session());
3693 switch (_operation) {
3694 case CreateRangeMarker:
3695 case CreateTransportMarker:
3696 case CreateCDMarker:
3698 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3703 cursor = _editor->cursors()->selector;
3707 Drag::start_grab (event, cursor);
3709 show_verbose_cursor_time (adjusted_current_frame (event));
3713 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3715 framepos_t start = 0;
3717 ArdourCanvas::SimpleRect *crect;
3719 switch (_operation) {
3720 case CreateRangeMarker:
3721 crect = _editor->range_bar_drag_rect;
3723 case CreateTransportMarker:
3724 crect = _editor->transport_bar_drag_rect;
3726 case CreateCDMarker:
3727 crect = _editor->cd_marker_bar_drag_rect;
3730 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3735 framepos_t const pf = adjusted_current_frame (event);
3737 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3738 framepos_t grab = grab_frame ();
3739 _editor->snap_to (grab);
3741 if (pf < grab_frame()) {
3749 /* first drag: Either add to the selection
3750 or create a new selection.
3755 _editor->temp_location->set (start, end);
3759 update_item (_editor->temp_location);
3761 //_drag_rect->raise_to_top();
3766 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3767 _editor->start_canvas_autoscroll (1, 0);
3771 _editor->temp_location->set (start, end);
3773 double x1 = _editor->frame_to_pixel (start);
3774 double x2 = _editor->frame_to_pixel (end);
3775 crect->property_x1() = x1;
3776 crect->property_x2() = x2;
3778 update_item (_editor->temp_location);
3781 show_verbose_cursor_time (pf);
3786 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3788 Location * newloc = 0;
3792 if (movement_occurred) {
3793 motion (event, false);
3796 switch (_operation) {
3797 case CreateRangeMarker:
3798 case CreateCDMarker:
3800 _editor->begin_reversible_command (_("new range marker"));
3801 XMLNode &before = _editor->session()->locations()->get_state();
3802 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3803 if (_operation == CreateCDMarker) {
3804 flags = Location::IsRangeMarker | Location::IsCDMarker;
3805 _editor->cd_marker_bar_drag_rect->hide();
3808 flags = Location::IsRangeMarker;
3809 _editor->range_bar_drag_rect->hide();
3811 newloc = new Location (
3812 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3815 _editor->session()->locations()->add (newloc, true);
3816 XMLNode &after = _editor->session()->locations()->get_state();
3817 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3818 _editor->commit_reversible_command ();
3822 case CreateTransportMarker:
3823 // popup menu to pick loop or punch
3824 _editor->new_transport_marker_context_menu (&event->button, _item);
3828 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3830 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3835 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3837 if (end == max_framepos) {
3838 end = _editor->session()->current_end_frame ();
3841 if (start == max_framepos) {
3842 start = _editor->session()->current_start_frame ();
3845 switch (_editor->mouse_mode) {
3847 /* find the two markers on either side and then make the selection from it */
3848 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3852 /* find the two markers on either side of the click and make the range out of it */
3853 _editor->selection->set (start, end);
3862 _editor->stop_canvas_autoscroll ();
3866 RangeMarkerBarDrag::aborted (bool)
3872 RangeMarkerBarDrag::update_item (Location* location)
3874 double const x1 = _editor->frame_to_pixel (location->start());
3875 double const x2 = _editor->frame_to_pixel (location->end());
3877 _drag_rect->property_x1() = x1;
3878 _drag_rect->property_x2() = x2;
3881 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3885 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3889 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3891 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3892 Drag::start_grab (event, _editor->cursors()->zoom_out);
3895 Drag::start_grab (event, _editor->cursors()->zoom_in);
3899 show_verbose_cursor_time (adjusted_current_frame (event));
3903 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3908 framepos_t const pf = adjusted_current_frame (event);
3910 framepos_t grab = grab_frame ();
3911 _editor->snap_to_with_modifier (grab, event);
3913 /* base start and end on initial click position */
3925 _editor->zoom_rect->show();
3926 _editor->zoom_rect->raise_to_top();
3929 _editor->reposition_zoom_rect(start, end);
3931 show_verbose_cursor_time (pf);
3936 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3938 if (movement_occurred) {
3939 motion (event, false);
3941 if (grab_frame() < last_pointer_frame()) {
3942 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3944 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3947 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3948 _editor->tav_zoom_step (_zoom_out);
3950 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3954 _editor->zoom_rect->hide();
3958 MouseZoomDrag::aborted (bool)
3960 _editor->zoom_rect->hide ();
3963 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3965 , _cumulative_dx (0)
3966 , _cumulative_dy (0)
3968 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3970 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3971 _region = &_primary->region_view ();
3972 _note_height = _region->midi_stream_view()->note_height ();
3976 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3978 Drag::start_grab (event);
3980 if (!(_was_selected = _primary->selected())) {
3982 /* tertiary-click means extend selection - we'll do that on button release,
3983 so don't add it here, because otherwise we make it hard to figure
3984 out the "extend-to" range.
3987 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3990 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3993 _region->note_selected (_primary, true);
3995 _region->unique_select (_primary);
4001 /** @return Current total drag x change in frames */
4003 NoteDrag::total_dx () const
4006 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
4008 /* primary note time */
4009 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4011 /* new time of the primary note in session frames */
4012 frameoffset_t st = n + dx;
4014 framepos_t const rp = _region->region()->position ();
4016 /* prevent the note being dragged earlier than the region's position */
4019 /* snap and return corresponding delta */
4020 return _region->snap_frame_to_frame (st - rp) + rp - n;
4023 /** @return Current total drag y change in note number */
4025 NoteDrag::total_dy () const
4027 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
4031 NoteDrag::motion (GdkEvent *, bool)
4033 /* Total change in x and y since the start of the drag */
4034 frameoffset_t const dx = total_dx ();
4035 int8_t const dy = total_dy ();
4037 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4038 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4039 double const tdy = -dy * _note_height - _cumulative_dy;
4042 _cumulative_dx += tdx;
4043 _cumulative_dy += tdy;
4045 int8_t note_delta = total_dy();
4047 _region->move_selection (tdx, tdy, note_delta);
4049 /* the new note value may be the same as the old one, but we
4050 * don't know what that means because the selection may have
4051 * involved more than one note and we might be doing something
4052 * odd with them. so show the note value anyway, always.
4056 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4058 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4059 (int) floor (new_note));
4061 show_verbose_cursor_text (buf);
4066 NoteDrag::finished (GdkEvent* ev, bool moved)
4069 if (_editor->current_mouse_mode() == Editing::MouseObject) {
4071 if (_was_selected) {
4072 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4074 _region->note_deselected (_primary);
4077 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4078 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4080 if (!extend && !add && _region->selection_size() > 1) {
4081 _region->unique_select (_primary);
4082 } else if (extend) {
4083 _region->note_selected (_primary, true, true);
4085 /* it was added during button press */
4090 _region->note_dropped (_primary, total_dx(), total_dy());
4095 NoteDrag::aborted (bool)
4100 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
4101 : Drag (editor, item)
4103 , _nothing_to_drag (false)
4105 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4107 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
4110 /* get all lines in the automation view */
4111 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
4113 /* find those that overlap the ranges being dragged */
4114 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
4115 while (i != lines.end ()) {
4116 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4119 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4121 /* check this range against all the AudioRanges that we are using */
4122 list<AudioRange>::const_iterator k = _ranges.begin ();
4123 while (k != _ranges.end()) {
4124 if (k->coverage (r.first, r.second) != OverlapNone) {
4130 /* add it to our list if it overlaps at all */
4131 if (k != _ranges.end()) {
4136 _lines.push_back (n);
4142 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4146 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4148 Drag::start_grab (event, cursor);
4150 /* Get line states before we start changing things */
4151 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4152 i->state = &i->line->get_state ();
4155 if (_ranges.empty()) {
4157 /* No selected time ranges: drag all points */
4158 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4159 uint32_t const N = i->line->npoints ();
4160 for (uint32_t j = 0; j < N; ++j) {
4161 i->points.push_back (i->line->nth (j));
4167 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4169 framecnt_t const half = (i->start + i->end) / 2;
4171 /* find the line that this audio range starts in */
4172 list<Line>::iterator j = _lines.begin();
4173 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4177 if (j != _lines.end()) {
4178 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4180 /* j is the line that this audio range starts in; fade into it;
4181 64 samples length plucked out of thin air.
4184 framepos_t a = i->start + 64;
4189 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4190 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4192 the_list->add (p, the_list->eval (p));
4193 j->line->add_always_in_view (p);
4194 the_list->add (q, the_list->eval (q));
4195 j->line->add_always_in_view (q);
4198 /* same thing for the end */
4201 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4205 if (j != _lines.end()) {
4206 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4208 /* j is the line that this audio range starts in; fade out of it;
4209 64 samples length plucked out of thin air.
4212 framepos_t b = i->end - 64;
4217 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4218 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4220 the_list->add (p, the_list->eval (p));
4221 j->line->add_always_in_view (p);
4222 the_list->add (q, the_list->eval (q));
4223 j->line->add_always_in_view (q);
4227 _nothing_to_drag = true;
4229 /* Find all the points that should be dragged and put them in the relevant
4230 points lists in the Line structs.
4233 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4235 uint32_t const N = i->line->npoints ();
4236 for (uint32_t j = 0; j < N; ++j) {
4238 /* here's a control point on this line */
4239 ControlPoint* p = i->line->nth (j);
4240 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4242 /* see if it's inside a range */
4243 list<AudioRange>::const_iterator k = _ranges.begin ();
4244 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4248 if (k != _ranges.end()) {
4249 /* dragging this point */
4250 _nothing_to_drag = false;
4251 i->points.push_back (p);
4257 if (_nothing_to_drag) {
4261 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4262 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4267 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4269 if (_nothing_to_drag) {
4273 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4274 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4276 /* we are ignoring x position for this drag, so we can just pass in anything */
4277 i->line->drag_motion (0, f, true, false);
4282 AutomationRangeDrag::finished (GdkEvent* event, bool)
4284 if (_nothing_to_drag) {
4288 motion (event, false);
4289 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4290 i->line->end_drag ();
4291 i->line->clear_always_in_view ();
4294 _editor->session()->commit_reversible_command ();
4298 AutomationRangeDrag::aborted (bool)
4300 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4301 i->line->clear_always_in_view ();
4306 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4309 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4310 layer = v->region()->layer ();
4311 initial_y = v->get_canvas_group()->property_y ();
4312 initial_playlist = v->region()->playlist ();
4313 initial_position = v->region()->position ();
4314 initial_end = v->region()->position () + v->region()->length ();
4317 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4321 , _cumulative_dx (0)
4323 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4327 PatchChangeDrag::motion (GdkEvent* ev, bool)
4329 framepos_t f = adjusted_current_frame (ev);
4330 boost::shared_ptr<Region> r = _region_view->region ();
4331 f = max (f, r->position ());
4332 f = min (f, r->last_frame ());
4334 framecnt_t const dxf = f - grab_frame();
4335 double const dxu = _editor->frame_to_unit (dxf);
4336 _patch_change->move (dxu - _cumulative_dx, 0);
4337 _cumulative_dx = dxu;
4341 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4343 if (!movement_occurred) {
4347 boost::shared_ptr<Region> r (_region_view->region ());
4349 framepos_t f = adjusted_current_frame (ev);
4350 f = max (f, r->position ());
4351 f = min (f, r->last_frame ());
4353 _region_view->move_patch_change (
4355 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4360 PatchChangeDrag::aborted (bool)
4362 _patch_change->move (-_cumulative_dx, 0);
4366 PatchChangeDrag::setup_pointer_frame_offset ()
4368 boost::shared_ptr<Region> region = _region_view->region ();
4369 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4372 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4373 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4380 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4382 framepos_t const p = _region_view->region()->position ();
4383 double const y = _region_view->midi_view()->y_position ();
4385 x1 = max ((framepos_t) 0, x1 - p);
4386 x2 = max ((framepos_t) 0, x2 - p);
4387 y1 = max (0.0, y1 - y);
4388 y2 = max (0.0, y2 - y);
4390 _region_view->update_drag_selection (
4391 _editor->frame_to_pixel (x1),
4392 _editor->frame_to_pixel (x2),
4395 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4400 MidiRubberbandSelectDrag::deselect_things ()
4405 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4406 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4409 _vertical_only = true;
4413 MidiVerticalSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4415 double const y = _region_view->midi_view()->y_position ();
4417 y1 = max (0.0, y1 - y);
4418 y2 = max (0.0, y2 - y);
4420 _region_view->update_vertical_drag_selection (
4423 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4428 MidiVerticalSelectDrag::deselect_things ()
4433 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4434 : RubberbandSelectDrag (e, i)
4440 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4442 if (drag_in_progress) {
4443 /* We just want to select things at the end of the drag, not during it */
4447 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4449 _editor->begin_reversible_command (_("rubberband selection"));
4450 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4451 _editor->commit_reversible_command ();
4455 EditorRubberbandSelectDrag::deselect_things ()
4457 if (!getenv("ARDOUR_SAE")) {
4458 _editor->selection->clear_tracks();
4460 _editor->selection->clear_regions();
4461 _editor->selection->clear_points ();
4462 _editor->selection->clear_lines ();
4465 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4473 NoteCreateDrag::~NoteCreateDrag ()
4479 NoteCreateDrag::grid_frames (framepos_t t) const
4482 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4487 return _region_view->region_beats_to_region_frames (grid_beats);
4491 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4493 Drag::start_grab (event, cursor);
4495 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4497 framepos_t pf = _drags->current_pointer_frame ();
4498 framecnt_t const g = grid_frames (pf);
4500 /* Hack so that we always snap to the note that we are over, instead of snapping
4501 to the next one if we're more than halfway through the one we're over.
4503 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4507 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4509 MidiStreamView* sv = _region_view->midi_stream_view ();
4510 double const x = _editor->frame_to_pixel (_note[0]);
4511 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4513 _drag_rect->property_x1() = x;
4514 _drag_rect->property_y1() = y;
4515 _drag_rect->property_x2() = x;
4516 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4518 _drag_rect->property_outline_what() = 0xff;
4519 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4520 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4524 NoteCreateDrag::motion (GdkEvent* event, bool)
4526 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4527 double const x = _editor->frame_to_pixel (_note[1]);
4528 if (_note[1] > _note[0]) {
4529 _drag_rect->property_x2() = x;
4531 _drag_rect->property_x1() = x;
4536 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4538 if (!had_movement) {
4542 framepos_t const start = min (_note[0], _note[1]);
4543 framecnt_t length = abs (_note[0] - _note[1]);
4545 framecnt_t const g = grid_frames (start);
4546 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4548 if (_editor->snap_mode() == SnapNormal && length < g) {
4549 length = g - one_tick;
4552 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4554 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4558 NoteCreateDrag::y_to_region (double y) const
4561 _region_view->get_canvas_group()->w2i (x, y);
4566 NoteCreateDrag::aborted (bool)