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/audioengine.h"
33 #include "ardour/audioregion.h"
34 #include "ardour/dB.h"
35 #include "ardour/midi_region.h"
36 #include "ardour/operations.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/session.h"
43 #include "audio_region_view.h"
44 #include "midi_region_view.h"
45 #include "ardour_ui.h"
46 #include "gui_thread.h"
47 #include "control_point.h"
49 #include "region_gain_line.h"
50 #include "editor_drag.h"
51 #include "audio_time_axis.h"
52 #include "midi_time_axis.h"
53 #include "canvas-note.h"
54 #include "selection.h"
55 #include "midi_selection.h"
56 #include "automation_time_axis.h"
58 #include "editor_cursors.h"
59 #include "mouse_cursors.h"
60 #include "verbose_cursor.h"
63 using namespace ARDOUR;
66 using namespace Gtkmm2ext;
67 using namespace Editing;
68 using namespace ArdourCanvas;
70 using Gtkmm2ext::Keyboard;
72 double ControlPointDrag::_zero_gain_fraction = -1.0;
74 DragManager::DragManager (Editor* e)
77 , _current_pointer_frame (0)
81 DragManager::~DragManager ()
86 /** Call abort for each active drag */
92 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
97 if (!_drags.empty ()) {
98 _editor->set_follow_playhead (_old_follow_playhead, false);
107 DragManager::add (Drag* d)
109 d->set_manager (this);
110 _drags.push_back (d);
114 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
116 d->set_manager (this);
117 _drags.push_back (d);
122 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
124 /* Prevent follow playhead during the drag to be nice to the user */
125 _old_follow_playhead = _editor->follow_playhead ();
126 _editor->set_follow_playhead (false);
128 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
130 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
131 (*i)->start_grab (e, c);
135 /** Call end_grab for each active drag.
136 * @return true if any drag reported movement having occurred.
139 DragManager::end_grab (GdkEvent* e)
144 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
145 bool const t = (*i)->end_grab (e);
156 _editor->set_follow_playhead (_old_follow_playhead, false);
162 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
166 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
168 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
169 bool const t = (*i)->motion_handler (e, from_autoscroll);
180 DragManager::have_item (ArdourCanvas::Item* i) const
182 list<Drag*>::const_iterator j = _drags.begin ();
183 while (j != _drags.end() && (*j)->item () != i) {
187 return j != _drags.end ();
190 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
193 , _pointer_frame_offset (0)
194 , _move_threshold_passed (false)
195 , _raw_grab_frame (0)
197 , _last_pointer_frame (0)
203 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
209 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time);
211 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
216 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
218 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
220 if (Keyboard::is_button2_event (&event->button)) {
221 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
222 _y_constrained = true;
223 _x_constrained = false;
225 _y_constrained = false;
226 _x_constrained = true;
229 _x_constrained = false;
230 _y_constrained = false;
233 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
234 setup_pointer_frame_offset ();
235 _grab_frame = adjusted_frame (_raw_grab_frame, event);
236 _last_pointer_frame = _grab_frame;
237 _last_pointer_x = _grab_x;
238 _last_pointer_y = _grab_y;
241 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
244 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
249 if (_editor->session() && _editor->session()->transport_rolling()) {
252 _was_rolling = false;
255 switch (_editor->snap_type()) {
256 case SnapToRegionStart:
257 case SnapToRegionEnd:
258 case SnapToRegionSync:
259 case SnapToRegionBoundary:
260 _editor->build_region_boundary_cache ();
267 /** Call to end a drag `successfully'. Ungrabs item and calls
268 * subclass' finished() method.
270 * @param event GDK event, or 0.
271 * @return true if some movement occurred, otherwise false.
274 Drag::end_grab (GdkEvent* event)
276 _editor->stop_canvas_autoscroll ();
278 _item->ungrab (event ? event->button.time : 0);
280 finished (event, _move_threshold_passed);
282 _editor->verbose_cursor()->hide ();
284 return _move_threshold_passed;
288 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
292 if (f > _pointer_frame_offset) {
293 pos = f - _pointer_frame_offset;
297 _editor->snap_to_with_modifier (pos, event);
304 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
306 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
310 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
312 /* check to see if we have moved in any way that matters since the last motion event */
313 if (_move_threshold_passed &&
314 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
315 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
319 pair<framecnt_t, int> const threshold = move_threshold ();
321 bool const old_move_threshold_passed = _move_threshold_passed;
323 if (!from_autoscroll && !_move_threshold_passed) {
325 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
326 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
328 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
331 if (active (_editor->mouse_mode) && _move_threshold_passed) {
333 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
334 if (!from_autoscroll) {
335 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
336 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
337 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
340 motion (event, _move_threshold_passed != old_move_threshold_passed);
342 _last_pointer_x = _drags->current_pointer_x ();
343 _last_pointer_y = _drags->current_pointer_y ();
344 _last_pointer_frame = adjusted_current_frame (event);
352 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
360 aborted (_move_threshold_passed);
362 _editor->stop_canvas_autoscroll ();
363 _editor->verbose_cursor()->hide ();
367 Drag::show_verbose_cursor_time (framepos_t frame)
369 _editor->verbose_cursor()->set_time (
371 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
372 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
375 _editor->verbose_cursor()->show ();
379 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
381 _editor->verbose_cursor()->show (xoffset);
383 _editor->verbose_cursor()->set_duration (
385 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
386 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
391 Drag::show_verbose_cursor_text (string const & text)
393 _editor->verbose_cursor()->show ();
395 _editor->verbose_cursor()->set (
397 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
398 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
402 boost::shared_ptr<Region>
403 Drag::add_midi_region (MidiTimeAxisView* view)
405 if (_editor->session()) {
406 const TempoMap& map (_editor->session()->tempo_map());
407 framecnt_t pos = grab_frame();
408 const Meter& m = map.meter_at (pos);
409 /* not that the frame rate used here can be affected by pull up/down which
412 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
413 return view->add_region (grab_frame(), len, true);
416 return boost::shared_ptr<Region>();
419 struct EditorOrderTimeAxisViewSorter {
420 bool operator() (TimeAxisView* a, TimeAxisView* b) {
421 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
422 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
424 return ra->route()->order_key (EditorSort) < rb->route()->order_key (EditorSort);
428 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
432 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
434 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
435 as some of the regions we are dragging may be on such tracks.
438 TrackViewList track_views = _editor->track_views;
439 track_views.sort (EditorOrderTimeAxisViewSorter ());
441 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
442 _time_axis_views.push_back (*i);
444 TimeAxisView::Children children_list = (*i)->get_child_list ();
445 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
446 _time_axis_views.push_back (j->get());
450 /* the list of views can be empty at this point if this is a region list-insert drag
453 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
454 _views.push_back (DraggingView (*i, this));
457 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
461 RegionDrag::region_going_away (RegionView* v)
463 list<DraggingView>::iterator i = _views.begin ();
464 while (i != _views.end() && i->view != v) {
468 if (i != _views.end()) {
473 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
474 * or -1 if it is not found.
477 RegionDrag::find_time_axis_view (TimeAxisView* t) const
480 int const N = _time_axis_views.size ();
481 while (i < N && _time_axis_views[i] != t) {
492 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
493 : RegionDrag (e, i, p, v),
502 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
504 Drag::start_grab (event, cursor);
506 show_verbose_cursor_time (_last_frame_position);
508 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
509 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
510 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
514 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
516 /* compute the amount of pointer motion in frames, and where
517 the region would be if we moved it by that much.
519 *pending_region_position = adjusted_current_frame (event);
521 framepos_t sync_frame;
522 framecnt_t sync_offset;
525 sync_offset = _primary->region()->sync_offset (sync_dir);
527 /* we don't handle a sync point that lies before zero.
529 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
531 sync_frame = *pending_region_position + (sync_dir*sync_offset);
533 _editor->snap_to_with_modifier (sync_frame, event);
535 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
538 *pending_region_position = _last_frame_position;
541 if (*pending_region_position > max_framepos - _primary->region()->length()) {
542 *pending_region_position = _last_frame_position;
547 /* in locked edit mode, reverse the usual meaning of _x_constrained */
548 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
550 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
552 /* x movement since last time (in pixels) */
553 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
555 /* total x movement */
556 framecnt_t total_dx = *pending_region_position;
557 if (regions_came_from_canvas()) {
558 total_dx = total_dx - grab_frame ();
561 /* check that no regions have gone off the start of the session */
562 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
563 if ((i->view->region()->position() + total_dx) < 0) {
565 *pending_region_position = _last_frame_position;
570 _last_frame_position = *pending_region_position;
577 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
579 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
580 int const n = i->time_axis_view + delta_track;
581 if (n < 0 || n >= int (_time_axis_views.size())) {
582 /* off the top or bottom track */
586 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
587 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
588 /* not a track, or the wrong type */
592 double const l = i->layer + delta_layer;
594 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
595 mode to allow the user to place a region below another on layer 0.
597 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
598 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
599 If it has, the layers will be munged later anyway, so it's ok.
605 /* all regions being dragged are ok with this change */
610 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
612 assert (!_views.empty ());
614 /* Find the TimeAxisView that the pointer is now over */
615 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
617 if (first_move && tv.first->view()->layer_display() == Stacked) {
618 tv.first->view()->set_layer_display (Expanded);
621 /* Bail early if we're not over a track */
622 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
623 if (!rtv || !rtv->is_track()) {
624 _editor->verbose_cursor()->hide ();
628 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
630 /* Here's the current pointer position in terms of time axis view and layer */
631 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
632 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
634 /* Work out the change in x */
635 framepos_t pending_region_position;
636 double const x_delta = compute_x_delta (event, &pending_region_position);
638 /* Work out the change in y */
639 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
640 double delta_layer = current_pointer_layer - _last_pointer_layer;
642 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
643 /* this y movement is not allowed, so do no y movement this time */
644 delta_time_axis_view = 0;
648 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
649 /* haven't reached next snap point, and we're not switching
650 trackviews nor layers. nothing to do.
655 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
657 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
659 RegionView* rv = i->view;
661 if (rv->region()->locked()) {
669 /* Absolutely no idea why this is necessary, but it is; without
670 it, the region view disappears after the reparent.
672 _editor->update_canvas_now ();
674 /* Reparent to a non scrolling group so that we can keep the
675 region selection above all time axis views.
676 Reparenting means that we will have to move the region view
677 later, as the two parent groups have different coordinates.
680 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
682 rv->fake_set_opaque (true);
685 /* If we have moved tracks, we'll fudge the layer delta so that the
686 region gets moved back onto layer 0 on its new track; this avoids
687 confusion when dragging regions from non-zero layers onto different
690 double this_delta_layer = delta_layer;
691 if (delta_time_axis_view != 0) {
692 this_delta_layer = - i->layer;
695 /* The TimeAxisView that this region is now on */
696 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
698 /* Ensure it is moved from stacked -> expanded if appropriate */
699 if (tv->view()->layer_display() == Stacked) {
700 tv->view()->set_layer_display (Expanded);
703 /* We're only allowed to go -ve in layer on Expanded views */
704 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
705 this_delta_layer = - i->layer;
709 rv->set_height (tv->view()->child_height ());
711 /* Update show/hidden status as the region view may have come from a hidden track,
712 or have moved to one.
715 rv->get_canvas_group()->hide ();
717 rv->get_canvas_group()->show ();
720 /* Update the DraggingView */
721 i->time_axis_view += delta_time_axis_view;
722 i->layer += this_delta_layer;
725 _editor->mouse_brush_insert_region (rv, pending_region_position);
730 /* Get the y coordinate of the top of the track that this region is now on */
731 tv->canvas_display()->i2w (x, y);
732 y += _editor->get_trackview_group_vertical_offset();
734 /* And adjust for the layer that it should be on */
735 StreamView* cv = tv->view ();
736 switch (cv->layer_display ()) {
740 y += (cv->layers() - i->layer - 1) * cv->child_height ();
743 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
747 /* Now move the region view */
748 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
751 } /* foreach region */
753 _total_x_delta += x_delta;
756 _editor->cursor_group->raise_to_top();
759 if (x_delta != 0 && !_brushing) {
760 show_verbose_cursor_time (_last_frame_position);
763 _last_pointer_time_axis_view += delta_time_axis_view;
764 _last_pointer_layer += delta_layer;
768 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
770 if (_copy && first_move) {
772 /* duplicate the regionview(s) and region(s) */
774 list<DraggingView> new_regionviews;
776 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
778 RegionView* rv = i->view;
779 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
780 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
782 const boost::shared_ptr<const Region> original = rv->region();
783 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
784 region_copy->set_position (original->position());
788 boost::shared_ptr<AudioRegion> audioregion_copy
789 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
791 nrv = new AudioRegionView (*arv, audioregion_copy);
793 boost::shared_ptr<MidiRegion> midiregion_copy
794 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
795 nrv = new MidiRegionView (*mrv, midiregion_copy);
800 nrv->get_canvas_group()->show ();
801 new_regionviews.push_back (DraggingView (nrv, this));
803 /* swap _primary to the copy */
805 if (rv == _primary) {
809 /* ..and deselect the one we copied */
811 rv->set_selected (false);
814 if (!new_regionviews.empty()) {
816 /* reflect the fact that we are dragging the copies */
818 _views = new_regionviews;
820 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
823 sync the canvas to what we think is its current state
824 without it, the canvas seems to
825 "forget" to update properly after the upcoming reparent()
826 ..only if the mouse is in rapid motion at the time of the grab.
827 something to do with regionview creation taking so long?
829 _editor->update_canvas_now();
833 RegionMotionDrag::motion (event, first_move);
837 RegionMotionDrag::finished (GdkEvent *, bool)
839 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
844 if ((*i)->view()->layer_display() == Expanded) {
845 (*i)->view()->set_layer_display (Stacked);
851 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
853 RegionMotionDrag::finished (ev, movement_occurred);
855 if (!movement_occurred) {
860 /* reverse this here so that we have the correct logic to finalize
864 if (Config->get_edit_mode() == Lock) {
865 _x_constrained = !_x_constrained;
868 assert (!_views.empty ());
870 /* We might have hidden region views so that they weren't visible during the drag
871 (when they have been reparented). Now everything can be shown again, as region
872 views are back in their track parent groups.
874 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
875 i->view->get_canvas_group()->show ();
878 bool const changed_position = (_last_frame_position != _primary->region()->position());
879 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
880 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
882 _editor->update_canvas_now ();
902 if (_editor->session() && Config->get_always_play_range()) {
903 _editor->session()->request_locate (_editor->get_selection().regions.start());
908 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
910 RegionSelection new_views;
911 PlaylistSet modified_playlists;
912 list<RegionView*> views_to_delete;
915 /* all changes were made during motion event handlers */
917 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
921 _editor->commit_reversible_command ();
925 if (_x_constrained) {
926 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
928 _editor->begin_reversible_command (Operations::region_copy);
931 /* insert the regions into their new playlists */
932 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
934 if (i->view->region()->locked()) {
940 if (changed_position && !_x_constrained) {
941 where = i->view->region()->position() - drag_delta;
943 where = i->view->region()->position();
946 RegionView* new_view = insert_region_into_playlist (
947 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
954 new_views.push_back (new_view);
956 /* we don't need the copied RegionView any more */
957 views_to_delete.push_back (i->view);
960 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
961 because when views are deleted they are automagically removed from _views, which messes
964 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
968 /* If we've created new regions either by copying or moving
969 to a new track, we want to replace the old selection with the new ones
972 if (new_views.size() > 0) {
973 _editor->selection->set (new_views);
976 /* write commands for the accumulated diffs for all our modified playlists */
977 add_stateful_diff_commands_for_playlists (modified_playlists);
979 _editor->commit_reversible_command ();
983 RegionMoveDrag::finished_no_copy (
984 bool const changed_position,
985 bool const changed_tracks,
986 framecnt_t const drag_delta
989 RegionSelection new_views;
990 PlaylistSet modified_playlists;
991 PlaylistSet frozen_playlists;
992 set<RouteTimeAxisView*> views_to_update;
995 /* all changes were made during motion event handlers */
996 _editor->commit_reversible_command ();
1000 if (_x_constrained) {
1001 _editor->begin_reversible_command (_("fixed time region drag"));
1003 _editor->begin_reversible_command (Operations::region_drag);
1006 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1008 RegionView* rv = i->view;
1010 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1011 double const dest_layer = i->layer;
1013 if (rv->region()->locked()) {
1018 views_to_update.insert (dest_rtv);
1022 if (changed_position && !_x_constrained) {
1023 where = rv->region()->position() - drag_delta;
1025 where = rv->region()->position();
1028 if (changed_tracks) {
1030 /* insert into new playlist */
1032 RegionView* new_view = insert_region_into_playlist (
1033 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1036 if (new_view == 0) {
1041 new_views.push_back (new_view);
1043 /* remove from old playlist */
1045 /* the region that used to be in the old playlist is not
1046 moved to the new one - we use a copy of it. as a result,
1047 any existing editor for the region should no longer be
1050 rv->hide_region_editor();
1051 rv->fake_set_opaque (false);
1053 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1057 rv->region()->clear_changes ();
1060 motion on the same track. plonk the previously reparented region
1061 back to its original canvas group (its streamview).
1062 No need to do anything for copies as they are fake regions which will be deleted.
1065 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1066 rv->get_canvas_group()->property_y() = i->initial_y;
1069 /* just change the model */
1071 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1073 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1074 playlist->set_layer (rv->region(), dest_layer);
1077 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1079 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1082 playlist->freeze ();
1085 /* this movement may result in a crossfade being modified, so we need to get undo
1086 data from the playlist as well as the region.
1089 r = modified_playlists.insert (playlist);
1091 playlist->clear_changes ();
1094 rv->region()->set_position (where);
1096 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1099 if (changed_tracks) {
1101 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1102 was selected in all of them, then removing it from a playlist will have removed all
1103 trace of it from _views (i.e. there were N regions selected, we removed 1,
1104 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1105 corresponding regionview, and _views is now empty).
1107 This could have invalidated any and all iterators into _views.
1109 The heuristic we use here is: if the region selection is empty, break out of the loop
1110 here. if the region selection is not empty, then restart the loop because we know that
1111 we must have removed at least the region(view) we've just been working on as well as any
1112 that we processed on previous iterations.
1114 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1115 we can just iterate.
1119 if (_views.empty()) {
1130 /* If we've created new regions either by copying or moving
1131 to a new track, we want to replace the old selection with the new ones
1134 if (new_views.size() > 0) {
1135 _editor->selection->set (new_views);
1138 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1142 /* write commands for the accumulated diffs for all our modified playlists */
1143 add_stateful_diff_commands_for_playlists (modified_playlists);
1145 _editor->commit_reversible_command ();
1147 /* We have futzed with the layering of canvas items on our streamviews.
1148 If any region changed layer, this will have resulted in the stream
1149 views being asked to set up their region views, and all will be well.
1150 If not, we might now have badly-ordered region views. Ask the StreamViews
1151 involved to sort themselves out, just in case.
1154 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1155 (*i)->view()->playlist_layered ((*i)->track ());
1159 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1160 * @param region Region to remove.
1161 * @param playlist playlist To remove from.
1162 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1163 * that clear_changes () is only called once per playlist.
1166 RegionMoveDrag::remove_region_from_playlist (
1167 boost::shared_ptr<Region> region,
1168 boost::shared_ptr<Playlist> playlist,
1169 PlaylistSet& modified_playlists
1172 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1175 playlist->clear_changes ();
1178 playlist->remove_region (region);
1182 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1183 * clearing the playlist's diff history first if necessary.
1184 * @param region Region to insert.
1185 * @param dest_rtv Destination RouteTimeAxisView.
1186 * @param dest_layer Destination layer.
1187 * @param where Destination position.
1188 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1189 * that clear_changes () is only called once per playlist.
1190 * @return New RegionView, or 0 if no insert was performed.
1193 RegionMoveDrag::insert_region_into_playlist (
1194 boost::shared_ptr<Region> region,
1195 RouteTimeAxisView* dest_rtv,
1198 PlaylistSet& modified_playlists
1201 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1202 if (!dest_playlist) {
1206 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1207 _new_region_view = 0;
1208 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1210 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1211 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1213 dest_playlist->clear_changes ();
1216 dest_playlist->add_region (region, where);
1218 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1219 dest_playlist->set_layer (region, dest_layer);
1224 assert (_new_region_view);
1226 return _new_region_view;
1230 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1232 _new_region_view = rv;
1236 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1238 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1239 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1241 _editor->session()->add_command (c);
1250 RegionMoveDrag::aborted (bool movement_occurred)
1254 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1261 RegionMotionDrag::aborted (movement_occurred);
1266 RegionMotionDrag::aborted (bool)
1268 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1269 if ((*i)->view()->layer_display() == Expanded) {
1270 (*i)->view()->set_layer_display (Stacked);
1274 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1275 RegionView* rv = i->view;
1276 TimeAxisView* tv = &(rv->get_time_axis_view ());
1277 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1279 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1280 rv->get_canvas_group()->property_y() = 0;
1282 rv->fake_set_opaque (false);
1283 rv->move (-_total_x_delta, 0);
1284 rv->set_height (rtv->view()->child_height ());
1287 _editor->update_canvas_now ();
1290 /** @param b true to brush, otherwise false.
1291 * @param c true to make copies of the regions being moved, otherwise false.
1293 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1294 : RegionMotionDrag (e, i, p, v, b),
1297 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1300 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1301 if (rtv && rtv->is_track()) {
1302 speed = rtv->track()->speed ();
1305 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1309 RegionMoveDrag::setup_pointer_frame_offset ()
1311 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1314 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1315 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1317 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1319 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1320 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1322 _primary = v->view()->create_region_view (r, false, false);
1324 _primary->get_canvas_group()->show ();
1325 _primary->set_position (pos, 0);
1326 _views.push_back (DraggingView (_primary, this));
1328 _last_frame_position = pos;
1330 _item = _primary->get_canvas_group ();
1334 RegionInsertDrag::finished (GdkEvent *, bool)
1336 _editor->update_canvas_now ();
1338 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1340 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1341 _primary->get_canvas_group()->property_y() = 0;
1343 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1345 _editor->begin_reversible_command (Operations::insert_region);
1346 playlist->clear_changes ();
1347 playlist->add_region (_primary->region (), _last_frame_position);
1348 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1349 _editor->commit_reversible_command ();
1357 RegionInsertDrag::aborted (bool)
1364 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1365 : RegionMoveDrag (e, i, p, v, false, false)
1367 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1370 struct RegionSelectionByPosition {
1371 bool operator() (RegionView*a, RegionView* b) {
1372 return a->region()->position () < b->region()->position();
1377 RegionSpliceDrag::motion (GdkEvent* event, bool)
1379 /* Which trackview is this ? */
1381 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1382 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1384 /* The region motion is only processed if the pointer is over
1388 if (!tv || !tv->is_track()) {
1389 /* To make sure we hide the verbose canvas cursor when the mouse is
1390 not held over and audiotrack.
1392 _editor->verbose_cursor()->hide ();
1398 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1404 RegionSelection copy (_editor->selection->regions);
1406 RegionSelectionByPosition cmp;
1409 framepos_t const pf = adjusted_current_frame (event);
1411 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1413 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1419 boost::shared_ptr<Playlist> playlist;
1421 if ((playlist = atv->playlist()) == 0) {
1425 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1430 if (pf < (*i)->region()->last_frame() + 1) {
1434 if (pf > (*i)->region()->first_frame()) {
1440 playlist->shuffle ((*i)->region(), dir);
1445 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1447 RegionMoveDrag::finished (event, movement_occurred);
1451 RegionSpliceDrag::aborted (bool)
1456 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1458 _view (dynamic_cast<MidiTimeAxisView*> (v))
1460 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1466 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1469 _region = add_midi_region (_view);
1470 _view->playlist()->freeze ();
1473 framepos_t const f = adjusted_current_frame (event);
1474 if (f < grab_frame()) {
1475 _region->set_position (f);
1478 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1479 so that if this region is duplicated, its duplicate starts on
1480 a snap point rather than 1 frame after a snap point. Otherwise things get
1481 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1482 place snapped notes at the start of the region.
1485 framecnt_t const len = abs (f - grab_frame () - 1);
1486 _region->set_length (len < 1 ? 1 : len);
1492 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1494 if (!movement_occurred) {
1495 add_midi_region (_view);
1497 _view->playlist()->thaw ();
1502 RegionCreateDrag::aborted (bool)
1505 _view->playlist()->thaw ();
1511 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1515 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1519 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1521 Gdk::Cursor* cursor;
1522 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1523 float x_fraction = cnote->mouse_x_fraction ();
1525 if (x_fraction > 0.0 && x_fraction < 0.25) {
1526 cursor = _editor->cursors()->left_side_trim;
1528 cursor = _editor->cursors()->right_side_trim;
1531 Drag::start_grab (event, cursor);
1533 region = &cnote->region_view();
1535 double const region_start = region->get_position_pixels();
1536 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1538 if (grab_x() <= middle_point) {
1539 cursor = _editor->cursors()->left_side_trim;
1542 cursor = _editor->cursors()->right_side_trim;
1546 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1548 if (event->motion.state & Keyboard::PrimaryModifier) {
1554 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1556 if (ms.size() > 1) {
1557 /* has to be relative, may make no sense otherwise */
1561 /* select this note; if it is already selected, preserve the existing selection,
1562 otherwise make this note the only one selected.
1564 region->note_selected (cnote, cnote->selected ());
1566 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1567 MidiRegionSelection::iterator next;
1570 (*r)->begin_resizing (at_front);
1576 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1578 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1579 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1580 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1585 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1587 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1588 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1589 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1594 NoteResizeDrag::aborted (bool)
1596 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1597 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1598 (*r)->abort_resizing ();
1602 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1603 : RegionDrag (e, i, p, v)
1605 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1609 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1612 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1613 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1615 if (tv && tv->is_track()) {
1616 speed = tv->track()->speed();
1619 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1620 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1621 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1623 framepos_t const pf = adjusted_current_frame (event);
1625 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1626 /* Move the contents of the region around without changing the region bounds */
1627 _operation = ContentsTrim;
1628 Drag::start_grab (event, _editor->cursors()->trimmer);
1630 /* These will get overridden for a point trim.*/
1631 if (pf < (region_start + region_length/2)) {
1632 /* closer to front */
1633 _operation = StartTrim;
1634 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1637 _operation = EndTrim;
1638 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1642 switch (_operation) {
1644 show_verbose_cursor_time (region_start);
1645 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1646 i->view->trim_front_starting ();
1650 show_verbose_cursor_time (region_end);
1653 show_verbose_cursor_time (pf);
1657 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1658 i->view->region()->suspend_property_changes ();
1663 TrimDrag::motion (GdkEvent* event, bool first_move)
1665 RegionView* rv = _primary;
1668 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1669 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1670 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1672 if (tv && tv->is_track()) {
1673 speed = tv->track()->speed();
1676 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1682 switch (_operation) {
1684 trim_type = "Region start trim";
1687 trim_type = "Region end trim";
1690 trim_type = "Region content trim";
1694 _editor->begin_reversible_command (trim_type);
1696 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1697 RegionView* rv = i->view;
1698 rv->fake_set_opaque (false);
1699 rv->enable_display (false);
1700 rv->region()->playlist()->clear_owned_changes ();
1702 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1705 arv->temporarily_hide_envelope ();
1709 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1710 insert_result = _editor->motion_frozen_playlists.insert (pl);
1712 if (insert_result.second) {
1718 bool non_overlap_trim = false;
1720 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1721 non_overlap_trim = true;
1724 switch (_operation) {
1726 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1727 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1732 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1733 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1739 bool swap_direction = false;
1741 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1742 swap_direction = true;
1745 framecnt_t frame_delta = 0;
1747 bool left_direction = false;
1748 if (last_pointer_frame() > adjusted_current_frame(event)) {
1749 left_direction = true;
1752 if (left_direction) {
1753 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1755 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1758 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1759 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1765 switch (_operation) {
1767 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1770 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1773 show_verbose_cursor_time (adjusted_current_frame (event));
1780 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1782 if (movement_occurred) {
1783 motion (event, false);
1785 /* This must happen before the region's StatefulDiffCommand is created, as it may
1786 `correct' (ahem) the region's _start from being negative to being zero. It
1787 needs to be zero in the undo record.
1789 if (_operation == StartTrim) {
1790 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1791 i->view->trim_front_ending ();
1795 if (!_editor->selection->selected (_primary)) {
1796 _primary->thaw_after_trim ();
1799 set<boost::shared_ptr<Playlist> > diffed_playlists;
1801 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1802 i->view->thaw_after_trim ();
1803 i->view->enable_display (true);
1804 i->view->fake_set_opaque (true);
1806 /* Trimming one region may affect others on the playlist, so we need
1807 to get undo Commands from the whole playlist rather than just the
1808 region. Use diffed_playlists to make sure we don't diff a given
1809 playlist more than once.
1811 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1812 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1813 vector<Command*> cmds;
1815 _editor->session()->add_commands (cmds);
1816 diffed_playlists.insert (p);
1820 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1824 _editor->motion_frozen_playlists.clear ();
1825 _editor->commit_reversible_command();
1827 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1828 i->view->drag_end ();
1832 /* no mouse movement */
1833 _editor->point_trim (event, adjusted_current_frame (event));
1836 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1837 if (_operation == StartTrim) {
1838 i->view->trim_front_ending ();
1841 i->view->region()->resume_property_changes ();
1846 TrimDrag::aborted (bool movement_occurred)
1848 /* Our motion method is changing model state, so use the Undo system
1849 to cancel. Perhaps not ideal, as this will leave an Undo point
1850 behind which may be slightly odd from the user's point of view.
1855 if (movement_occurred) {
1859 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1860 i->view->region()->resume_property_changes ();
1865 TrimDrag::setup_pointer_frame_offset ()
1867 list<DraggingView>::iterator i = _views.begin ();
1868 while (i != _views.end() && i->view != _primary) {
1872 if (i == _views.end()) {
1876 switch (_operation) {
1878 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1881 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1888 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1892 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1893 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1898 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1900 Drag::start_grab (event, cursor);
1901 show_verbose_cursor_time (adjusted_current_frame(event));
1905 MeterMarkerDrag::setup_pointer_frame_offset ()
1907 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1911 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1915 // create a dummy marker for visual representation of moving the
1916 // section, because whether its a copy or not, we're going to
1917 // leave or lose the original marker (leave if its a copy; lose if its
1918 // not, because we'll remove it from the map).
1920 MeterSection section (_marker->meter());
1922 if (!section.movable()) {
1927 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1929 _marker = new MeterMarker (
1931 *_editor->meter_group,
1932 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1934 *new MeterSection (_marker->meter())
1937 /* use the new marker for the grab */
1938 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1941 TempoMap& map (_editor->session()->tempo_map());
1942 /* get current state */
1943 before_state = &map.get_state();
1944 /* remove the section while we drag it */
1945 map.remove_meter (section, true);
1949 framepos_t const pf = adjusted_current_frame (event);
1950 _marker->set_position (pf);
1951 show_verbose_cursor_time (pf);
1955 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1957 if (!movement_occurred) {
1961 motion (event, false);
1963 Timecode::BBT_Time when;
1965 TempoMap& map (_editor->session()->tempo_map());
1966 map.bbt_time (last_pointer_frame(), when);
1968 if (_copy == true) {
1969 _editor->begin_reversible_command (_("copy meter mark"));
1970 XMLNode &before = map.get_state();
1971 map.add_meter (_marker->meter(), when);
1972 XMLNode &after = map.get_state();
1973 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1974 _editor->commit_reversible_command ();
1977 _editor->begin_reversible_command (_("move meter mark"));
1979 /* we removed it before, so add it back now */
1981 map.add_meter (_marker->meter(), when);
1982 XMLNode &after = map.get_state();
1983 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
1984 _editor->commit_reversible_command ();
1987 // delete the dummy marker we used for visual representation while moving.
1988 // a new visual marker will show up automatically.
1993 MeterMarkerDrag::aborted (bool moved)
1995 _marker->set_position (_marker->meter().frame ());
1998 TempoMap& map (_editor->session()->tempo_map());
1999 /* we removed it before, so add it back now */
2000 map.add_meter (_marker->meter(), _marker->meter().frame());
2001 // delete the dummy marker we used for visual representation while moving.
2002 // a new visual marker will show up automatically.
2007 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2011 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2013 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2018 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2020 Drag::start_grab (event, cursor);
2021 show_verbose_cursor_time (adjusted_current_frame (event));
2025 TempoMarkerDrag::setup_pointer_frame_offset ()
2027 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2031 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2035 // create a dummy marker for visual representation of moving the
2036 // section, because whether its a copy or not, we're going to
2037 // leave or lose the original marker (leave if its a copy; lose if its
2038 // not, because we'll remove it from the map).
2040 // create a dummy marker for visual representation of moving the copy.
2041 // The actual copying is not done before we reach the finish callback.
2044 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2046 TempoSection section (_marker->tempo());
2048 _marker = new TempoMarker (
2050 *_editor->tempo_group,
2051 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2053 *new TempoSection (_marker->tempo())
2056 /* use the new marker for the grab */
2057 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2060 TempoMap& map (_editor->session()->tempo_map());
2061 /* get current state */
2062 before_state = &map.get_state();
2063 /* remove the section while we drag it */
2064 map.remove_tempo (section, true);
2068 framepos_t const pf = adjusted_current_frame (event);
2069 _marker->set_position (pf);
2070 show_verbose_cursor_time (pf);
2074 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2076 if (!movement_occurred) {
2080 motion (event, false);
2082 TempoMap& map (_editor->session()->tempo_map());
2083 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2084 Timecode::BBT_Time when;
2086 map.bbt_time (beat_time, when);
2088 if (_copy == true) {
2089 _editor->begin_reversible_command (_("copy tempo mark"));
2090 XMLNode &before = map.get_state();
2091 map.add_tempo (_marker->tempo(), when);
2092 XMLNode &after = map.get_state();
2093 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2094 _editor->commit_reversible_command ();
2097 _editor->begin_reversible_command (_("move tempo mark"));
2098 /* we removed it before, so add it back now */
2099 map.add_tempo (_marker->tempo(), when);
2100 XMLNode &after = map.get_state();
2101 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2102 _editor->commit_reversible_command ();
2105 // delete the dummy marker we used for visual representation while moving.
2106 // a new visual marker will show up automatically.
2111 TempoMarkerDrag::aborted (bool moved)
2113 _marker->set_position (_marker->tempo().frame());
2115 TempoMap& map (_editor->session()->tempo_map());
2116 /* we removed it before, so add it back now */
2117 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2118 // delete the dummy marker we used for visual representation while moving.
2119 // a new visual marker will show up automatically.
2124 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2128 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2131 /** Do all the things we do when dragging the playhead to make it look as though
2132 * we have located, without actually doing the locate (because that would cause
2133 * the diskstream buffers to be refilled, which is too slow).
2136 CursorDrag::fake_locate (framepos_t t)
2138 _editor->playhead_cursor->set_position (t);
2140 Session* s = _editor->session ();
2141 if (s->timecode_transmission_suspended ()) {
2142 framepos_t const f = _editor->playhead_cursor->current_frame;
2143 s->send_mmc_locate (f);
2144 s->send_full_time_code (f);
2147 show_verbose_cursor_time (t);
2148 _editor->UpdateAllTransportClocks (t);
2152 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2154 Drag::start_grab (event, c);
2156 _grab_zoom = _editor->frames_per_unit;
2158 framepos_t where = _editor->event_frame (event, 0, 0);
2159 _editor->snap_to_with_modifier (where, event);
2161 _editor->_dragging_playhead = true;
2163 Session* s = _editor->session ();
2166 if (_was_rolling && _stop) {
2170 if (s->is_auditioning()) {
2171 s->cancel_audition ();
2175 if (AudioEngine::instance()->connected()) {
2177 /* do this only if we're the engine is connected
2178 * because otherwise this request will never be
2179 * serviced and we'll busy wait forever. likewise,
2180 * notice if we are disconnected while waiting for the
2181 * request to be serviced.
2184 s->request_suspend_timecode_transmission ();
2185 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2186 /* twiddle our thumbs */
2191 fake_locate (where);
2195 CursorDrag::motion (GdkEvent* event, bool)
2197 framepos_t const adjusted_frame = adjusted_current_frame (event);
2198 if (adjusted_frame != last_pointer_frame()) {
2199 fake_locate (adjusted_frame);
2201 _editor->update_canvas_now ();
2207 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2209 _editor->_dragging_playhead = false;
2211 if (!movement_occurred && _stop) {
2215 motion (event, false);
2217 Session* s = _editor->session ();
2219 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2220 _editor->_pending_locate_request = true;
2221 s->request_resume_timecode_transmission ();
2226 CursorDrag::aborted (bool)
2228 if (_editor->_dragging_playhead) {
2229 _editor->session()->request_resume_timecode_transmission ();
2230 _editor->_dragging_playhead = false;
2233 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2236 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2237 : RegionDrag (e, i, p, v)
2239 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2243 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2245 Drag::start_grab (event, cursor);
2247 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2248 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2250 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2252 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2256 FadeInDrag::setup_pointer_frame_offset ()
2258 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2259 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2260 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2264 FadeInDrag::motion (GdkEvent* event, bool)
2266 framecnt_t fade_length;
2268 framepos_t const pos = adjusted_current_frame (event);
2270 boost::shared_ptr<Region> region = _primary->region ();
2272 if (pos < (region->position() + 64)) {
2273 fade_length = 64; // this should be a minimum defined somewhere
2274 } else if (pos > region->last_frame()) {
2275 fade_length = region->length();
2277 fade_length = pos - region->position();
2280 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2282 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2288 tmp->reset_fade_in_shape_width (fade_length);
2289 tmp->show_fade_line((framecnt_t) fade_length);
2292 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2296 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2298 if (!movement_occurred) {
2302 framecnt_t fade_length;
2304 framepos_t const pos = adjusted_current_frame (event);
2306 boost::shared_ptr<Region> region = _primary->region ();
2308 if (pos < (region->position() + 64)) {
2309 fade_length = 64; // this should be a minimum defined somewhere
2310 } else if (pos > region->last_frame()) {
2311 fade_length = region->length();
2313 fade_length = pos - region->position();
2316 _editor->begin_reversible_command (_("change fade in length"));
2318 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2320 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2326 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2327 XMLNode &before = alist->get_state();
2329 tmp->audio_region()->set_fade_in_length (fade_length);
2330 tmp->audio_region()->set_fade_in_active (true);
2331 tmp->hide_fade_line();
2333 XMLNode &after = alist->get_state();
2334 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2337 _editor->commit_reversible_command ();
2341 FadeInDrag::aborted (bool)
2343 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2344 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2350 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2351 tmp->hide_fade_line();
2355 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2356 : RegionDrag (e, i, p, v)
2358 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2362 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2364 Drag::start_grab (event, cursor);
2366 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2367 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2369 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2371 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2375 FadeOutDrag::setup_pointer_frame_offset ()
2377 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2378 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2379 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2383 FadeOutDrag::motion (GdkEvent* event, bool)
2385 framecnt_t fade_length;
2387 framepos_t const pos = adjusted_current_frame (event);
2389 boost::shared_ptr<Region> region = _primary->region ();
2391 if (pos > (region->last_frame() - 64)) {
2392 fade_length = 64; // this should really be a minimum fade defined somewhere
2394 else if (pos < region->position()) {
2395 fade_length = region->length();
2398 fade_length = region->last_frame() - pos;
2401 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2403 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2409 tmp->reset_fade_out_shape_width (fade_length);
2410 tmp->show_fade_line(region->length() - fade_length);
2413 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2417 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2419 if (!movement_occurred) {
2423 framecnt_t fade_length;
2425 framepos_t const pos = adjusted_current_frame (event);
2427 boost::shared_ptr<Region> region = _primary->region ();
2429 if (pos > (region->last_frame() - 64)) {
2430 fade_length = 64; // this should really be a minimum fade defined somewhere
2432 else if (pos < region->position()) {
2433 fade_length = region->length();
2436 fade_length = region->last_frame() - pos;
2439 _editor->begin_reversible_command (_("change fade out length"));
2441 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2443 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2449 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2450 XMLNode &before = alist->get_state();
2452 tmp->audio_region()->set_fade_out_length (fade_length);
2453 tmp->audio_region()->set_fade_out_active (true);
2454 tmp->hide_fade_line();
2456 XMLNode &after = alist->get_state();
2457 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2460 _editor->commit_reversible_command ();
2464 FadeOutDrag::aborted (bool)
2466 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2467 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2473 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2474 tmp->hide_fade_line();
2478 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2481 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2483 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2486 _points.push_back (Gnome::Art::Point (0, 0));
2487 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2490 MarkerDrag::~MarkerDrag ()
2492 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2498 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2500 Drag::start_grab (event, cursor);
2504 Location *location = _editor->find_location_from_marker (_marker, is_start);
2505 _editor->_dragging_edit_point = true;
2507 update_item (location);
2509 // _drag_line->show();
2510 // _line->raise_to_top();
2513 show_verbose_cursor_time (location->start());
2515 show_verbose_cursor_time (location->end());
2518 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2521 case Selection::Toggle:
2522 _editor->selection->toggle (_marker);
2524 case Selection::Set:
2525 if (!_editor->selection->selected (_marker)) {
2526 _editor->selection->set (_marker);
2529 case Selection::Extend:
2531 Locations::LocationList ll;
2532 list<Marker*> to_add;
2534 _editor->selection->markers.range (s, e);
2535 s = min (_marker->position(), s);
2536 e = max (_marker->position(), e);
2539 if (e < max_framepos) {
2542 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2543 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2544 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2547 to_add.push_back (lm->start);
2550 to_add.push_back (lm->end);
2554 if (!to_add.empty()) {
2555 _editor->selection->add (to_add);
2559 case Selection::Add:
2560 _editor->selection->add (_marker);
2564 /* Set up copies for us to manipulate during the drag */
2566 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2567 Location* l = _editor->find_location_from_marker (*i, is_start);
2568 _copied_locations.push_back (new Location (*l));
2573 MarkerDrag::setup_pointer_frame_offset ()
2576 Location *location = _editor->find_location_from_marker (_marker, is_start);
2577 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2581 MarkerDrag::motion (GdkEvent* event, bool)
2583 framecnt_t f_delta = 0;
2585 bool move_both = false;
2587 Location *real_location;
2588 Location *copy_location = 0;
2590 framepos_t const newframe = adjusted_current_frame (event);
2592 framepos_t next = newframe;
2594 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2598 MarkerSelection::iterator i;
2599 list<Location*>::iterator x;
2601 /* find the marker we're dragging, and compute the delta */
2603 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2604 x != _copied_locations.end() && i != _editor->selection->markers.end();
2610 if (marker == _marker) {
2612 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2617 if (real_location->is_mark()) {
2618 f_delta = newframe - copy_location->start();
2622 switch (marker->type()) {
2623 case Marker::SessionStart:
2624 case Marker::RangeStart:
2625 case Marker::LoopStart:
2626 case Marker::PunchIn:
2627 f_delta = newframe - copy_location->start();
2630 case Marker::SessionEnd:
2631 case Marker::RangeEnd:
2632 case Marker::LoopEnd:
2633 case Marker::PunchOut:
2634 f_delta = newframe - copy_location->end();
2637 /* what kind of marker is this ? */
2645 if (i == _editor->selection->markers.end()) {
2646 /* hmm, impossible - we didn't find the dragged marker */
2650 /* now move them all */
2652 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2653 x != _copied_locations.end() && i != _editor->selection->markers.end();
2659 /* call this to find out if its the start or end */
2661 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2665 if (real_location->locked()) {
2669 if (copy_location->is_mark()) {
2673 copy_location->set_start (copy_location->start() + f_delta);
2677 framepos_t new_start = copy_location->start() + f_delta;
2678 framepos_t new_end = copy_location->end() + f_delta;
2680 if (is_start) { // start-of-range marker
2683 copy_location->set_start (new_start);
2684 copy_location->set_end (new_end);
2685 } else if (new_start < copy_location->end()) {
2686 copy_location->set_start (new_start);
2687 } else if (newframe > 0) {
2688 _editor->snap_to (next, 1, true);
2689 copy_location->set_end (next);
2690 copy_location->set_start (newframe);
2693 } else { // end marker
2696 copy_location->set_end (new_end);
2697 copy_location->set_start (new_start);
2698 } else if (new_end > copy_location->start()) {
2699 copy_location->set_end (new_end);
2700 } else if (newframe > 0) {
2701 _editor->snap_to (next, -1, true);
2702 copy_location->set_start (next);
2703 copy_location->set_end (newframe);
2708 update_item (copy_location);
2710 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2713 lm->set_position (copy_location->start(), copy_location->end());
2717 assert (!_copied_locations.empty());
2719 show_verbose_cursor_time (newframe);
2722 _editor->update_canvas_now ();
2727 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2729 if (!movement_occurred) {
2731 /* just a click, do nothing but finish
2732 off the selection process
2735 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2738 case Selection::Set:
2739 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2740 _editor->selection->set (_marker);
2744 case Selection::Toggle:
2745 case Selection::Extend:
2746 case Selection::Add:
2753 _editor->_dragging_edit_point = false;
2755 _editor->begin_reversible_command ( _("move marker") );
2756 XMLNode &before = _editor->session()->locations()->get_state();
2758 MarkerSelection::iterator i;
2759 list<Location*>::iterator x;
2762 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2763 x != _copied_locations.end() && i != _editor->selection->markers.end();
2766 Location * location = _editor->find_location_from_marker (*i, is_start);
2770 if (location->locked()) {
2774 if (location->is_mark()) {
2775 location->set_start ((*x)->start());
2777 location->set ((*x)->start(), (*x)->end());
2782 XMLNode &after = _editor->session()->locations()->get_state();
2783 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2784 _editor->commit_reversible_command ();
2788 MarkerDrag::aborted (bool)
2794 MarkerDrag::update_item (Location*)
2799 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2801 _cumulative_x_drag (0),
2802 _cumulative_y_drag (0)
2804 if (_zero_gain_fraction < 0.0) {
2805 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2808 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2810 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2816 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2818 Drag::start_grab (event, _editor->cursors()->fader);
2820 // start the grab at the center of the control point so
2821 // the point doesn't 'jump' to the mouse after the first drag
2822 _fixed_grab_x = _point->get_x();
2823 _fixed_grab_y = _point->get_y();
2825 float const fraction = 1 - (_point->get_y() / _point->line().height());
2827 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2829 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2830 event->button.x + 10, event->button.y + 10);
2832 _editor->verbose_cursor()->show ();
2834 if (!_point->can_slide ()) {
2835 _x_constrained = true;
2840 ControlPointDrag::motion (GdkEvent* event, bool)
2842 double dx = _drags->current_pointer_x() - last_pointer_x();
2843 double dy = _drags->current_pointer_y() - last_pointer_y();
2845 if (event->button.state & Keyboard::SecondaryModifier) {
2850 /* coordinate in pixels relative to the start of the region (for region-based automation)
2851 or track (for track-based automation) */
2852 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2853 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2855 // calculate zero crossing point. back off by .01 to stay on the
2856 // positive side of zero
2857 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2859 // make sure we hit zero when passing through
2860 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2864 if (_x_constrained) {
2867 if (_y_constrained) {
2871 _cumulative_x_drag = cx - _fixed_grab_x;
2872 _cumulative_y_drag = cy - _fixed_grab_y;
2876 cy = min ((double) _point->line().height(), cy);
2878 framepos_t cx_frames = _editor->unit_to_frame (cx);
2880 if (!_x_constrained) {
2881 _editor->snap_to_with_modifier (cx_frames, event);
2884 cx_frames = min (cx_frames, _point->line().maximum_time());
2886 float const fraction = 1.0 - (cy / _point->line().height());
2888 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2890 _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, push);
2892 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2896 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2898 if (!movement_occurred) {
2902 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2903 _editor->reset_point_selection ();
2907 motion (event, false);
2910 _point->line().end_drag ();
2911 _editor->session()->commit_reversible_command ();
2915 ControlPointDrag::aborted (bool)
2917 _point->line().reset ();
2921 ControlPointDrag::active (Editing::MouseMode m)
2923 if (m == Editing::MouseGain) {
2924 /* always active in mouse gain */
2928 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2929 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2932 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2935 _cumulative_y_drag (0)
2937 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2941 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2943 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2946 _item = &_line->grab_item ();
2948 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2949 origin, and ditto for y.
2952 double cx = event->button.x;
2953 double cy = event->button.y;
2955 _line->parent_group().w2i (cx, cy);
2957 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2962 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2963 /* no adjacent points */
2967 Drag::start_grab (event, _editor->cursors()->fader);
2969 /* store grab start in parent frame */
2974 double fraction = 1.0 - (cy / _line->height());
2976 _line->start_drag_line (before, after, fraction);
2978 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2979 event->button.x + 10, event->button.y + 10);
2981 _editor->verbose_cursor()->show ();
2985 LineDrag::motion (GdkEvent* event, bool)
2987 double dy = _drags->current_pointer_y() - last_pointer_y();
2989 if (event->button.state & Keyboard::SecondaryModifier) {
2993 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2995 _cumulative_y_drag = cy - _fixed_grab_y;
2998 cy = min ((double) _line->height(), cy);
3000 double const fraction = 1.0 - (cy / _line->height());
3001 bool const push = !Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3003 /* we are ignoring x position for this drag, so we can just pass in anything */
3004 _line->drag_motion (0, fraction, true, push);
3006 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3010 LineDrag::finished (GdkEvent* event, bool)
3012 motion (event, false);
3014 _editor->session()->commit_reversible_command ();
3018 LineDrag::aborted (bool)
3023 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3026 _cumulative_x_drag (0)
3028 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3032 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3034 Drag::start_grab (event);
3036 _line = reinterpret_cast<Line*> (_item);
3039 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3041 double cx = event->button.x;
3042 double cy = event->button.y;
3044 _item->property_parent().get_value()->w2i(cx, cy);
3046 /* store grab start in parent frame */
3047 _region_view_grab_x = cx;
3049 _before = *(float*) _item->get_data ("position");
3051 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3053 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3057 FeatureLineDrag::motion (GdkEvent*, bool)
3059 double dx = _drags->current_pointer_x() - last_pointer_x();
3061 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3063 _cumulative_x_drag += dx;
3065 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3074 ArdourCanvas::Points points;
3076 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3078 _line->get_bounds(x1, y2, x2, y2);
3080 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3081 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3083 _line->property_points() = points;
3085 float *pos = new float;
3088 _line->set_data ("position", pos);
3094 FeatureLineDrag::finished (GdkEvent*, bool)
3096 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3097 _arv->update_transient(_before, _before);
3101 FeatureLineDrag::aborted (bool)
3106 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3108 , _vertical_only (false)
3110 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3114 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3116 Drag::start_grab (event);
3117 show_verbose_cursor_time (adjusted_current_frame (event));
3121 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3128 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3130 framepos_t grab = grab_frame ();
3131 if (Config->get_rubberbanding_snaps_to_grid ()) {
3132 _editor->snap_to_with_modifier (grab, event);
3135 /* base start and end on initial click position */
3145 if (_drags->current_pointer_y() < grab_y()) {
3146 y1 = _drags->current_pointer_y();
3149 y2 = _drags->current_pointer_y();
3154 if (start != end || y1 != y2) {
3156 double x1 = _editor->frame_to_pixel (start);
3157 double x2 = _editor->frame_to_pixel (end);
3159 _editor->rubberband_rect->property_x1() = x1;
3160 if (_vertical_only) {
3161 /* fixed 10 pixel width */
3162 _editor->rubberband_rect->property_x2() = x1 + 10;
3164 _editor->rubberband_rect->property_x2() = x2;
3167 _editor->rubberband_rect->property_y1() = y1;
3168 _editor->rubberband_rect->property_y2() = y2;
3170 _editor->rubberband_rect->show();
3171 _editor->rubberband_rect->raise_to_top();
3173 show_verbose_cursor_time (pf);
3175 do_select_things (event, true);
3180 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3185 if (grab_frame() < last_pointer_frame()) {
3187 x2 = last_pointer_frame ();
3190 x1 = last_pointer_frame ();
3196 if (_drags->current_pointer_y() < grab_y()) {
3197 y1 = _drags->current_pointer_y();
3200 y2 = _drags->current_pointer_y();
3204 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3208 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3210 if (movement_occurred) {
3212 motion (event, false);
3213 do_select_things (event, false);
3219 bool do_deselect = true;
3220 MidiTimeAxisView* mtv;
3222 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3224 if (_editor->selection->empty()) {
3225 /* nothing selected */
3226 add_midi_region (mtv);
3227 do_deselect = false;
3237 _editor->rubberband_rect->hide();
3241 RubberbandSelectDrag::aborted (bool)
3243 _editor->rubberband_rect->hide ();
3246 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3247 : RegionDrag (e, i, p, v)
3249 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3253 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3255 Drag::start_grab (event, cursor);
3257 show_verbose_cursor_time (adjusted_current_frame (event));
3261 TimeFXDrag::motion (GdkEvent* event, bool)
3263 RegionView* rv = _primary;
3264 StreamView* cv = rv->get_time_axis_view().view ();
3266 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3267 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3268 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3270 framepos_t const pf = adjusted_current_frame (event);
3272 if (pf > rv->region()->position()) {
3273 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3276 show_verbose_cursor_time (pf);
3280 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3282 _primary->get_time_axis_view().hide_timestretch ();
3284 if (!movement_occurred) {
3288 if (last_pointer_frame() < _primary->region()->position()) {
3289 /* backwards drag of the left edge - not usable */
3293 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3295 float percentage = (double) newlen / (double) _primary->region()->length();
3297 #ifndef USE_RUBBERBAND
3298 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3299 if (_primary->region()->data_type() == DataType::AUDIO) {
3300 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3304 if (!_editor->get_selection().regions.empty()) {
3305 /* primary will already be included in the selection, and edit
3306 group shared editing will propagate selection across
3307 equivalent regions, so just use the current region
3311 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3312 error << _("An error occurred while executing time stretch operation") << endmsg;
3318 TimeFXDrag::aborted (bool)
3320 _primary->get_time_axis_view().hide_timestretch ();
3323 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3326 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3330 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3332 Drag::start_grab (event);
3336 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3338 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3342 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3344 if (movement_occurred && _editor->session()) {
3345 /* make sure we stop */
3346 _editor->session()->request_transport_speed (0.0);
3351 ScrubDrag::aborted (bool)
3356 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3360 , _original_pointer_time_axis (-1)
3361 , _last_pointer_time_axis (-1)
3362 , _time_selection_at_start (!_editor->get_selection().time.empty())
3364 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3368 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3370 if (_editor->session() == 0) {
3374 Gdk::Cursor* cursor = 0;
3376 switch (_operation) {
3377 case CreateSelection:
3378 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3383 cursor = _editor->cursors()->selector;
3384 Drag::start_grab (event, cursor);
3387 case SelectionStartTrim:
3388 if (_editor->clicked_axisview) {
3389 _editor->clicked_axisview->order_selection_trims (_item, true);
3391 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3394 case SelectionEndTrim:
3395 if (_editor->clicked_axisview) {
3396 _editor->clicked_axisview->order_selection_trims (_item, false);
3398 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3402 Drag::start_grab (event, cursor);
3406 if (_operation == SelectionMove) {
3407 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3409 show_verbose_cursor_time (adjusted_current_frame (event));
3412 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3416 SelectionDrag::setup_pointer_frame_offset ()
3418 switch (_operation) {
3419 case CreateSelection:
3420 _pointer_frame_offset = 0;
3423 case SelectionStartTrim:
3425 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3428 case SelectionEndTrim:
3429 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3435 SelectionDrag::motion (GdkEvent* event, bool first_move)
3437 framepos_t start = 0;
3441 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3442 if (pending_time_axis.first == 0) {
3446 framepos_t const pending_position = adjusted_current_frame (event);
3448 /* only alter selection if things have changed */
3450 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3454 switch (_operation) {
3455 case CreateSelection:
3457 framepos_t grab = grab_frame ();
3460 _editor->snap_to (grab);
3463 if (pending_position < grab_frame()) {
3464 start = pending_position;
3467 end = pending_position;
3471 /* first drag: Either add to the selection
3472 or create a new selection
3478 /* adding to the selection */
3479 _editor->set_selected_track_as_side_effect (Selection::Add);
3480 //_editor->selection->add (_editor->clicked_axisview);
3481 _editor->clicked_selection = _editor->selection->add (start, end);
3486 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3487 //_editor->selection->set (_editor->clicked_axisview);
3488 _editor->set_selected_track_as_side_effect (Selection::Set);
3491 _editor->clicked_selection = _editor->selection->set (start, end);
3495 /* select the track that we're in */
3496 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3497 // _editor->set_selected_track_as_side_effect (Selection::Add);
3498 _editor->selection->add (pending_time_axis.first);
3499 _added_time_axes.push_back (pending_time_axis.first);
3502 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3503 tracks that we selected in the first place.
3506 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3507 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3509 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3510 while (i != _added_time_axes.end()) {
3512 list<TimeAxisView*>::iterator tmp = i;
3515 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3516 _editor->selection->remove (*i);
3517 _added_time_axes.remove (*i);
3526 case SelectionStartTrim:
3528 start = _editor->selection->time[_editor->clicked_selection].start;
3529 end = _editor->selection->time[_editor->clicked_selection].end;
3531 if (pending_position > end) {
3534 start = pending_position;
3538 case SelectionEndTrim:
3540 start = _editor->selection->time[_editor->clicked_selection].start;
3541 end = _editor->selection->time[_editor->clicked_selection].end;
3543 if (pending_position < start) {
3546 end = pending_position;
3553 start = _editor->selection->time[_editor->clicked_selection].start;
3554 end = _editor->selection->time[_editor->clicked_selection].end;
3556 length = end - start;
3558 start = pending_position;
3559 _editor->snap_to (start);
3561 end = start + length;
3566 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3567 _editor->start_canvas_autoscroll (1, 0);
3571 _editor->selection->replace (_editor->clicked_selection, start, end);
3574 if (_operation == SelectionMove) {
3575 show_verbose_cursor_time(start);
3577 show_verbose_cursor_time(pending_position);
3582 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3584 Session* s = _editor->session();
3586 if (movement_occurred) {
3587 motion (event, false);
3588 /* XXX this is not object-oriented programming at all. ick */
3589 if (_editor->selection->time.consolidate()) {
3590 _editor->selection->TimeChanged ();
3593 /* XXX what if its a music time selection? */
3595 if ((s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3596 s->request_play_range (&_editor->selection->time, true);
3598 if (Config->get_always_play_range()) {
3599 if (_editor->doing_range_stuff()) {
3600 s->request_locate (_editor->get_selection().time.start());
3607 /* just a click, no pointer movement.
3610 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3611 if (!_time_selection_at_start) {
3612 if (_editor->clicked_regionview) {
3613 if (_editor->get_selection().selected (_editor->clicked_regionview)) {
3614 /* range select the entire current
3617 _editor->select_range (_editor->get_selection().regions.start(),
3618 _editor->get_selection().regions.end_frame());
3620 /* range select this (unselected)
3623 _editor->select_range (_editor->clicked_regionview->region()->position(),
3624 _editor->clicked_regionview->region()->last_frame());
3628 _editor->selection->clear_time();
3632 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3633 _editor->selection->set (_editor->clicked_axisview);
3636 if (s && s->get_play_range () && s->transport_rolling()) {
3637 s->request_stop (false, false);
3640 if (Config->get_always_play_range()) {
3641 if (_editor->doing_range_stuff()) {
3642 s->request_locate (_editor->get_selection().time.start());
3647 _editor->stop_canvas_autoscroll ();
3651 SelectionDrag::aborted (bool)
3656 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3661 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3663 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3664 physical_screen_height (_editor->get_window()));
3665 _drag_rect->hide ();
3667 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3668 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3672 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3674 if (_editor->session() == 0) {
3678 Gdk::Cursor* cursor = 0;
3680 if (!_editor->temp_location) {
3681 _editor->temp_location = new Location (*_editor->session());
3684 switch (_operation) {
3685 case CreateRangeMarker:
3686 case CreateTransportMarker:
3687 case CreateCDMarker:
3689 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3694 cursor = _editor->cursors()->selector;
3698 Drag::start_grab (event, cursor);
3700 show_verbose_cursor_time (adjusted_current_frame (event));
3704 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3706 framepos_t start = 0;
3708 ArdourCanvas::SimpleRect *crect;
3710 switch (_operation) {
3711 case CreateRangeMarker:
3712 crect = _editor->range_bar_drag_rect;
3714 case CreateTransportMarker:
3715 crect = _editor->transport_bar_drag_rect;
3717 case CreateCDMarker:
3718 crect = _editor->cd_marker_bar_drag_rect;
3721 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
3726 framepos_t const pf = adjusted_current_frame (event);
3728 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3729 framepos_t grab = grab_frame ();
3730 _editor->snap_to (grab);
3732 if (pf < grab_frame()) {
3740 /* first drag: Either add to the selection
3741 or create a new selection.
3746 _editor->temp_location->set (start, end);
3750 update_item (_editor->temp_location);
3752 //_drag_rect->raise_to_top();
3757 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3758 _editor->start_canvas_autoscroll (1, 0);
3762 _editor->temp_location->set (start, end);
3764 double x1 = _editor->frame_to_pixel (start);
3765 double x2 = _editor->frame_to_pixel (end);
3766 crect->property_x1() = x1;
3767 crect->property_x2() = x2;
3769 update_item (_editor->temp_location);
3772 show_verbose_cursor_time (pf);
3777 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3779 Location * newloc = 0;
3783 if (movement_occurred) {
3784 motion (event, false);
3787 switch (_operation) {
3788 case CreateRangeMarker:
3789 case CreateCDMarker:
3791 _editor->begin_reversible_command (_("new range marker"));
3792 XMLNode &before = _editor->session()->locations()->get_state();
3793 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3794 if (_operation == CreateCDMarker) {
3795 flags = Location::IsRangeMarker | Location::IsCDMarker;
3796 _editor->cd_marker_bar_drag_rect->hide();
3799 flags = Location::IsRangeMarker;
3800 _editor->range_bar_drag_rect->hide();
3802 newloc = new Location (
3803 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3806 _editor->session()->locations()->add (newloc, true);
3807 XMLNode &after = _editor->session()->locations()->get_state();
3808 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3809 _editor->commit_reversible_command ();
3813 case CreateTransportMarker:
3814 // popup menu to pick loop or punch
3815 _editor->new_transport_marker_context_menu (&event->button, _item);
3819 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3821 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3826 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3828 if (end == max_framepos) {
3829 end = _editor->session()->current_end_frame ();
3832 if (start == max_framepos) {
3833 start = _editor->session()->current_start_frame ();
3836 switch (_editor->mouse_mode) {
3838 /* find the two markers on either side and then make the selection from it */
3839 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3843 /* find the two markers on either side of the click and make the range out of it */
3844 _editor->selection->set (start, end);
3853 _editor->stop_canvas_autoscroll ();
3857 RangeMarkerBarDrag::aborted (bool)
3863 RangeMarkerBarDrag::update_item (Location* location)
3865 double const x1 = _editor->frame_to_pixel (location->start());
3866 double const x2 = _editor->frame_to_pixel (location->end());
3868 _drag_rect->property_x1() = x1;
3869 _drag_rect->property_x2() = x2;
3872 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3876 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3880 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3882 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3883 Drag::start_grab (event, _editor->cursors()->zoom_out);
3886 Drag::start_grab (event, _editor->cursors()->zoom_in);
3890 show_verbose_cursor_time (adjusted_current_frame (event));
3894 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3899 framepos_t const pf = adjusted_current_frame (event);
3901 framepos_t grab = grab_frame ();
3902 _editor->snap_to_with_modifier (grab, event);
3904 /* base start and end on initial click position */
3916 _editor->zoom_rect->show();
3917 _editor->zoom_rect->raise_to_top();
3920 _editor->reposition_zoom_rect(start, end);
3922 show_verbose_cursor_time (pf);
3927 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3929 if (movement_occurred) {
3930 motion (event, false);
3932 if (grab_frame() < last_pointer_frame()) {
3933 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
3935 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
3938 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3939 _editor->tav_zoom_step (_zoom_out);
3941 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3945 _editor->zoom_rect->hide();
3949 MouseZoomDrag::aborted (bool)
3951 _editor->zoom_rect->hide ();
3954 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3956 , _cumulative_dx (0)
3957 , _cumulative_dy (0)
3959 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3961 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3962 _region = &_primary->region_view ();
3963 _note_height = _region->midi_stream_view()->note_height ();
3967 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3969 Drag::start_grab (event);
3971 if (!(_was_selected = _primary->selected())) {
3973 /* tertiary-click means extend selection - we'll do that on button release,
3974 so don't add it here, because otherwise we make it hard to figure
3975 out the "extend-to" range.
3978 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3981 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3984 _region->note_selected (_primary, true);
3986 _region->unique_select (_primary);
3992 /** @return Current total drag x change in frames */
3994 NoteDrag::total_dx () const
3997 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3999 /* primary note time */
4000 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4002 /* new time of the primary note in session frames */
4003 frameoffset_t st = n + dx;
4005 framepos_t const rp = _region->region()->position ();
4007 /* prevent the note being dragged earlier than the region's position */
4010 /* snap and return corresponding delta */
4011 return _region->snap_frame_to_frame (st - rp) + rp - n;
4014 /** @return Current total drag y change in note number */
4016 NoteDrag::total_dy () const
4018 MidiStreamView* msv = _region->midi_stream_view ();
4019 double const y = _region->midi_view()->y_position ();
4020 /* new current note */
4021 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4023 n = max (msv->lowest_note(), n);
4024 n = min (msv->highest_note(), n);
4025 /* and work out delta */
4026 return n - msv->y_to_note (grab_y() - y);
4030 NoteDrag::motion (GdkEvent *, bool)
4032 /* Total change in x and y since the start of the drag */
4033 frameoffset_t const dx = total_dx ();
4034 int8_t const dy = total_dy ();
4036 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4037 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4038 double const tdy = -dy * _note_height - _cumulative_dy;
4041 _cumulative_dx += tdx;
4042 _cumulative_dy += tdy;
4044 int8_t note_delta = total_dy();
4046 _region->move_selection (tdx, tdy, note_delta);
4048 /* the new note value may be the same as the old one, but we
4049 * don't know what that means because the selection may have
4050 * involved more than one note and we might be doing something
4051 * odd with them. so show the note value anyway, always.
4055 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4057 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4058 (int) floor (new_note));
4060 show_verbose_cursor_text (buf);
4065 NoteDrag::finished (GdkEvent* ev, bool moved)
4068 /* no motion - select note */
4070 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4071 _editor->current_mouse_mode() == Editing::MouseDraw) {
4073 if (_was_selected) {
4074 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4076 _region->note_deselected (_primary);
4079 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4080 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4082 if (!extend && !add && _region->selection_size() > 1) {
4083 _region->unique_select (_primary);
4084 } else if (extend) {
4085 _region->note_selected (_primary, true, true);
4087 /* it was added during button press */
4092 _region->note_dropped (_primary, total_dx(), total_dy());
4097 NoteDrag::aborted (bool)
4102 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4103 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4104 : Drag (editor, atv->base_item ())
4106 , _nothing_to_drag (false)
4108 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4109 y_origin = atv->y_position();
4110 setup (atv->lines ());
4113 /** Make an AutomationRangeDrag for region gain lines */
4114 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4115 : Drag (editor, rv->get_canvas_group ())
4117 , _nothing_to_drag (false)
4119 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4121 list<boost::shared_ptr<AutomationLine> > lines;
4122 lines.push_back (rv->get_gain_line ());
4123 y_origin = rv->get_time_axis_view().y_position();
4127 /** @param lines AutomationLines to drag.
4128 * @param offset Offset from the session start to the points in the AutomationLines.
4131 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4133 /* find the lines that overlap the ranges being dragged */
4134 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4135 while (i != lines.end ()) {
4136 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4139 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4141 /* check this range against all the AudioRanges that we are using */
4142 list<AudioRange>::const_iterator k = _ranges.begin ();
4143 while (k != _ranges.end()) {
4144 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4150 /* add it to our list if it overlaps at all */
4151 if (k != _ranges.end()) {
4156 _lines.push_back (n);
4162 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4166 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4168 return 1.0 - ((global_y - y_origin) / line->height());
4172 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4174 Drag::start_grab (event, cursor);
4176 /* Get line states before we start changing things */
4177 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4178 i->state = &i->line->get_state ();
4179 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4182 if (_ranges.empty()) {
4184 /* No selected time ranges: drag all points */
4185 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4186 uint32_t const N = i->line->npoints ();
4187 for (uint32_t j = 0; j < N; ++j) {
4188 i->points.push_back (i->line->nth (j));
4194 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4196 framecnt_t const half = (i->start + i->end) / 2;
4198 /* find the line that this audio range starts in */
4199 list<Line>::iterator j = _lines.begin();
4200 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4204 if (j != _lines.end()) {
4205 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4207 /* j is the line that this audio range starts in; fade into it;
4208 64 samples length plucked out of thin air.
4211 framepos_t a = i->start + 64;
4216 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4217 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4219 the_list->add (p, the_list->eval (p));
4220 the_list->add (q, the_list->eval (q));
4223 /* same thing for the end */
4226 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4230 if (j != _lines.end()) {
4231 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4233 /* j is the line that this audio range starts in; fade out of it;
4234 64 samples length plucked out of thin air.
4237 framepos_t b = i->end - 64;
4242 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4243 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4245 the_list->add (p, the_list->eval (p));
4246 the_list->add (q, the_list->eval (q));
4250 _nothing_to_drag = true;
4252 /* Find all the points that should be dragged and put them in the relevant
4253 points lists in the Line structs.
4256 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4258 uint32_t const N = i->line->npoints ();
4259 for (uint32_t j = 0; j < N; ++j) {
4261 /* here's a control point on this line */
4262 ControlPoint* p = i->line->nth (j);
4263 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4265 /* see if it's inside a range */
4266 list<AudioRange>::const_iterator k = _ranges.begin ();
4267 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4271 if (k != _ranges.end()) {
4272 /* dragging this point */
4273 _nothing_to_drag = false;
4274 i->points.push_back (p);
4280 if (_nothing_to_drag) {
4284 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4285 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4290 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4292 if (_nothing_to_drag) {
4296 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4297 float const f = y_fraction (l->line, _drags->current_pointer_y());
4299 /* we are ignoring x position for this drag, so we can just pass in anything */
4300 l->line->drag_motion (0, f, true, false);
4301 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4306 AutomationRangeDrag::finished (GdkEvent* event, bool)
4308 if (_nothing_to_drag) {
4312 motion (event, false);
4313 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4314 i->line->end_drag ();
4317 _editor->session()->commit_reversible_command ();
4321 AutomationRangeDrag::aborted (bool)
4323 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4328 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4331 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4332 layer = v->region()->layer ();
4333 initial_y = v->get_canvas_group()->property_y ();
4334 initial_playlist = v->region()->playlist ();
4335 initial_position = v->region()->position ();
4336 initial_end = v->region()->position () + v->region()->length ();
4339 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4343 , _cumulative_dx (0)
4345 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4346 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4351 PatchChangeDrag::motion (GdkEvent* ev, bool)
4353 framepos_t f = adjusted_current_frame (ev);
4354 boost::shared_ptr<Region> r = _region_view->region ();
4355 f = max (f, r->position ());
4356 f = min (f, r->last_frame ());
4358 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4359 double const dxu = _editor->frame_to_unit (dxf); // permitted fx in units
4360 _patch_change->move (dxu - _cumulative_dx, 0);
4361 _cumulative_dx = dxu;
4365 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4367 if (!movement_occurred) {
4371 boost::shared_ptr<Region> r (_region_view->region ());
4372 framepos_t f = adjusted_current_frame (ev);
4373 f = max (f, r->position ());
4374 f = min (f, r->last_frame ());
4376 _region_view->move_patch_change (
4378 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4383 PatchChangeDrag::aborted (bool)
4385 _patch_change->move (-_cumulative_dx, 0);
4389 PatchChangeDrag::setup_pointer_frame_offset ()
4391 boost::shared_ptr<Region> region = _region_view->region ();
4392 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4395 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4396 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4403 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4405 framepos_t const p = _region_view->region()->position ();
4406 double const y = _region_view->midi_view()->y_position ();
4408 x1 = max ((framepos_t) 0, x1 - p);
4409 x2 = max ((framepos_t) 0, x2 - p);
4410 y1 = max (0.0, y1 - y);
4411 y2 = max (0.0, y2 - y);
4413 _region_view->update_drag_selection (
4414 _editor->frame_to_pixel (x1),
4415 _editor->frame_to_pixel (x2),
4418 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4423 MidiRubberbandSelectDrag::deselect_things ()
4428 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4429 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4432 _vertical_only = true;
4436 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4438 double const y = _region_view->midi_view()->y_position ();
4440 y1 = max (0.0, y1 - y);
4441 y2 = max (0.0, y2 - y);
4443 _region_view->update_vertical_drag_selection (
4446 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4451 MidiVerticalSelectDrag::deselect_things ()
4456 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4457 : RubberbandSelectDrag (e, i)
4463 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4465 if (drag_in_progress) {
4466 /* We just want to select things at the end of the drag, not during it */
4470 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4472 _editor->begin_reversible_command (_("rubberband selection"));
4473 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4474 _editor->commit_reversible_command ();
4478 EditorRubberbandSelectDrag::deselect_things ()
4480 if (!getenv("ARDOUR_SAE")) {
4481 _editor->selection->clear_tracks();
4483 _editor->selection->clear_regions();
4484 _editor->selection->clear_points ();
4485 _editor->selection->clear_lines ();
4488 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4496 NoteCreateDrag::~NoteCreateDrag ()
4502 NoteCreateDrag::grid_frames (framepos_t t) const
4505 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4510 return _region_view->region_beats_to_region_frames (grid_beats);
4514 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4516 Drag::start_grab (event, cursor);
4518 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4520 framepos_t pf = _drags->current_pointer_frame ();
4521 framecnt_t const g = grid_frames (pf);
4523 /* Hack so that we always snap to the note that we are over, instead of snapping
4524 to the next one if we're more than halfway through the one we're over.
4526 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4530 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4532 MidiStreamView* sv = _region_view->midi_stream_view ();
4533 double const x = _editor->frame_to_pixel (_note[0]);
4534 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4536 _drag_rect->property_x1() = x;
4537 _drag_rect->property_y1() = y;
4538 _drag_rect->property_x2() = x;
4539 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4541 _drag_rect->property_outline_what() = 0xff;
4542 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4543 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4547 NoteCreateDrag::motion (GdkEvent* event, bool)
4549 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4550 double const x = _editor->frame_to_pixel (_note[1]);
4551 if (_note[1] > _note[0]) {
4552 _drag_rect->property_x2() = x;
4554 _drag_rect->property_x1() = x;
4559 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4561 if (!had_movement) {
4565 framepos_t const start = min (_note[0], _note[1]);
4566 framecnt_t length = abs (_note[0] - _note[1]);
4568 framecnt_t const g = grid_frames (start);
4569 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4571 if (_editor->snap_mode() == SnapNormal && length < g) {
4572 length = g - one_tick;
4575 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4577 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4581 NoteCreateDrag::y_to_region (double y) const
4584 _region_view->get_canvas_group()->w2i (x, y);
4589 NoteCreateDrag::aborted (bool)
4594 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4602 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4604 Drag::start_grab (event, cursor);
4608 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4614 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4617 distance = _drags->current_pointer_x() - grab_x();
4618 len = ar->fade_in()->back()->when;
4620 distance = grab_x() - _drags->current_pointer_x();
4621 len = ar->fade_out()->back()->when;
4624 /* how long should it be ? */
4626 new_length = len + _editor->unit_to_frame (distance);
4628 /* now check with the region that this is legal */
4630 new_length = ar->verify_xfade_bounds (new_length, start);
4633 arv->redraw_start_xfade_to (ar, new_length);
4635 arv->redraw_end_xfade_to (ar, new_length);
4640 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4646 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4649 distance = _drags->current_pointer_x() - grab_x();
4650 len = ar->fade_in()->back()->when;
4652 distance = grab_x() - _drags->current_pointer_x();
4653 len = ar->fade_out()->back()->when;
4656 new_length = ar->verify_xfade_bounds (len + _editor->unit_to_frame (distance), start);
4658 _editor->begin_reversible_command ("xfade trim");
4659 ar->playlist()->clear_owned_changes ();
4662 ar->set_fade_in_length (new_length);
4664 ar->set_fade_out_length (new_length);
4667 /* Adjusting the xfade may affect other regions in the playlist, so we need
4668 to get undo Commands from the whole playlist rather than just the
4672 vector<Command*> cmds;
4673 ar->playlist()->rdiff (cmds);
4674 _editor->session()->add_commands (cmds);
4675 _editor->commit_reversible_command ();
4680 CrossfadeEdgeDrag::aborted (bool)
4683 arv->redraw_start_xfade ();
4685 arv->redraw_end_xfade ();