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 ();
2174 if (AudioEngine::instance()->connected()) {
2175 s->request_suspend_timecode_transmission ();
2176 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2177 /* twiddle our thumbs */
2182 fake_locate (where);
2186 CursorDrag::motion (GdkEvent* event, bool)
2188 framepos_t const adjusted_frame = adjusted_current_frame (event);
2189 if (adjusted_frame != last_pointer_frame()) {
2190 fake_locate (adjusted_frame);
2192 _editor->update_canvas_now ();
2198 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2200 _editor->_dragging_playhead = false;
2202 if (!movement_occurred && _stop) {
2206 motion (event, false);
2208 Session* s = _editor->session ();
2210 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2211 _editor->_pending_locate_request = true;
2212 s->request_resume_timecode_transmission ();
2217 CursorDrag::aborted (bool)
2219 if (_editor->_dragging_playhead) {
2220 _editor->session()->request_resume_timecode_transmission ();
2221 _editor->_dragging_playhead = false;
2224 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2227 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2228 : RegionDrag (e, i, p, v)
2230 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2234 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2236 Drag::start_grab (event, cursor);
2238 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2239 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2241 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2243 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2247 FadeInDrag::setup_pointer_frame_offset ()
2249 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2250 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2251 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2255 FadeInDrag::motion (GdkEvent* event, bool)
2257 framecnt_t fade_length;
2259 framepos_t const pos = adjusted_current_frame (event);
2261 boost::shared_ptr<Region> region = _primary->region ();
2263 if (pos < (region->position() + 64)) {
2264 fade_length = 64; // this should be a minimum defined somewhere
2265 } else if (pos > region->last_frame()) {
2266 fade_length = region->length();
2268 fade_length = pos - region->position();
2271 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2273 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2279 tmp->reset_fade_in_shape_width (fade_length);
2280 tmp->show_fade_line((framecnt_t) fade_length);
2283 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2287 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2289 if (!movement_occurred) {
2293 framecnt_t fade_length;
2295 framepos_t const pos = adjusted_current_frame (event);
2297 boost::shared_ptr<Region> region = _primary->region ();
2299 if (pos < (region->position() + 64)) {
2300 fade_length = 64; // this should be a minimum defined somewhere
2301 } else if (pos > region->last_frame()) {
2302 fade_length = region->length();
2304 fade_length = pos - region->position();
2307 _editor->begin_reversible_command (_("change fade in length"));
2309 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2311 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2317 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2318 XMLNode &before = alist->get_state();
2320 tmp->audio_region()->set_fade_in_length (fade_length);
2321 tmp->audio_region()->set_fade_in_active (true);
2322 tmp->hide_fade_line();
2324 XMLNode &after = alist->get_state();
2325 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2328 _editor->commit_reversible_command ();
2332 FadeInDrag::aborted (bool)
2334 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2335 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2341 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2342 tmp->hide_fade_line();
2346 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2347 : RegionDrag (e, i, p, v)
2349 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2353 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2355 Drag::start_grab (event, cursor);
2357 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2358 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2360 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2362 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2366 FadeOutDrag::setup_pointer_frame_offset ()
2368 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2369 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2370 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2374 FadeOutDrag::motion (GdkEvent* event, bool)
2376 framecnt_t fade_length;
2378 framepos_t const pos = adjusted_current_frame (event);
2380 boost::shared_ptr<Region> region = _primary->region ();
2382 if (pos > (region->last_frame() - 64)) {
2383 fade_length = 64; // this should really be a minimum fade defined somewhere
2385 else if (pos < region->position()) {
2386 fade_length = region->length();
2389 fade_length = region->last_frame() - pos;
2392 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2394 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2400 tmp->reset_fade_out_shape_width (fade_length);
2401 tmp->show_fade_line(region->length() - fade_length);
2404 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2408 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2410 if (!movement_occurred) {
2414 framecnt_t fade_length;
2416 framepos_t const pos = adjusted_current_frame (event);
2418 boost::shared_ptr<Region> region = _primary->region ();
2420 if (pos > (region->last_frame() - 64)) {
2421 fade_length = 64; // this should really be a minimum fade defined somewhere
2423 else if (pos < region->position()) {
2424 fade_length = region->length();
2427 fade_length = region->last_frame() - pos;
2430 _editor->begin_reversible_command (_("change fade out length"));
2432 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2434 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2440 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2441 XMLNode &before = alist->get_state();
2443 tmp->audio_region()->set_fade_out_length (fade_length);
2444 tmp->audio_region()->set_fade_out_active (true);
2445 tmp->hide_fade_line();
2447 XMLNode &after = alist->get_state();
2448 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2451 _editor->commit_reversible_command ();
2455 FadeOutDrag::aborted (bool)
2457 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2458 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2464 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2465 tmp->hide_fade_line();
2469 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2472 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2474 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2477 _points.push_back (Gnome::Art::Point (0, 0));
2478 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2481 MarkerDrag::~MarkerDrag ()
2483 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2489 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2491 Drag::start_grab (event, cursor);
2495 Location *location = _editor->find_location_from_marker (_marker, is_start);
2496 _editor->_dragging_edit_point = true;
2498 update_item (location);
2500 // _drag_line->show();
2501 // _line->raise_to_top();
2504 show_verbose_cursor_time (location->start());
2506 show_verbose_cursor_time (location->end());
2509 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2512 case Selection::Toggle:
2513 _editor->selection->toggle (_marker);
2515 case Selection::Set:
2516 if (!_editor->selection->selected (_marker)) {
2517 _editor->selection->set (_marker);
2520 case Selection::Extend:
2522 Locations::LocationList ll;
2523 list<Marker*> to_add;
2525 _editor->selection->markers.range (s, e);
2526 s = min (_marker->position(), s);
2527 e = max (_marker->position(), e);
2530 if (e < max_framepos) {
2533 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2534 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2535 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2538 to_add.push_back (lm->start);
2541 to_add.push_back (lm->end);
2545 if (!to_add.empty()) {
2546 _editor->selection->add (to_add);
2550 case Selection::Add:
2551 _editor->selection->add (_marker);
2555 /* Set up copies for us to manipulate during the drag */
2557 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2558 Location* l = _editor->find_location_from_marker (*i, is_start);
2559 _copied_locations.push_back (new Location (*l));
2564 MarkerDrag::setup_pointer_frame_offset ()
2567 Location *location = _editor->find_location_from_marker (_marker, is_start);
2568 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2572 MarkerDrag::motion (GdkEvent* event, bool)
2574 framecnt_t f_delta = 0;
2576 bool move_both = false;
2578 Location *real_location;
2579 Location *copy_location = 0;
2581 framepos_t const newframe = adjusted_current_frame (event);
2583 framepos_t next = newframe;
2585 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2589 MarkerSelection::iterator i;
2590 list<Location*>::iterator x;
2592 /* find the marker we're dragging, and compute the delta */
2594 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2595 x != _copied_locations.end() && i != _editor->selection->markers.end();
2601 if (marker == _marker) {
2603 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2608 if (real_location->is_mark()) {
2609 f_delta = newframe - copy_location->start();
2613 switch (marker->type()) {
2614 case Marker::SessionStart:
2615 case Marker::RangeStart:
2616 case Marker::LoopStart:
2617 case Marker::PunchIn:
2618 f_delta = newframe - copy_location->start();
2621 case Marker::SessionEnd:
2622 case Marker::RangeEnd:
2623 case Marker::LoopEnd:
2624 case Marker::PunchOut:
2625 f_delta = newframe - copy_location->end();
2628 /* what kind of marker is this ? */
2636 if (i == _editor->selection->markers.end()) {
2637 /* hmm, impossible - we didn't find the dragged marker */
2641 /* now move them all */
2643 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2644 x != _copied_locations.end() && i != _editor->selection->markers.end();
2650 /* call this to find out if its the start or end */
2652 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2656 if (real_location->locked()) {
2660 if (copy_location->is_mark()) {
2664 copy_location->set_start (copy_location->start() + f_delta);
2668 framepos_t new_start = copy_location->start() + f_delta;
2669 framepos_t new_end = copy_location->end() + f_delta;
2671 if (is_start) { // start-of-range marker
2674 copy_location->set_start (new_start);
2675 copy_location->set_end (new_end);
2676 } else if (new_start < copy_location->end()) {
2677 copy_location->set_start (new_start);
2678 } else if (newframe > 0) {
2679 _editor->snap_to (next, 1, true);
2680 copy_location->set_end (next);
2681 copy_location->set_start (newframe);
2684 } else { // end marker
2687 copy_location->set_end (new_end);
2688 copy_location->set_start (new_start);
2689 } else if (new_end > copy_location->start()) {
2690 copy_location->set_end (new_end);
2691 } else if (newframe > 0) {
2692 _editor->snap_to (next, -1, true);
2693 copy_location->set_start (next);
2694 copy_location->set_end (newframe);
2699 update_item (copy_location);
2701 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2704 lm->set_position (copy_location->start(), copy_location->end());
2708 assert (!_copied_locations.empty());
2710 show_verbose_cursor_time (newframe);
2713 _editor->update_canvas_now ();
2718 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2720 if (!movement_occurred) {
2722 /* just a click, do nothing but finish
2723 off the selection process
2726 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2729 case Selection::Set:
2730 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2731 _editor->selection->set (_marker);
2735 case Selection::Toggle:
2736 case Selection::Extend:
2737 case Selection::Add:
2744 _editor->_dragging_edit_point = false;
2746 _editor->begin_reversible_command ( _("move marker") );
2747 XMLNode &before = _editor->session()->locations()->get_state();
2749 MarkerSelection::iterator i;
2750 list<Location*>::iterator x;
2753 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2754 x != _copied_locations.end() && i != _editor->selection->markers.end();
2757 Location * location = _editor->find_location_from_marker (*i, is_start);
2761 if (location->locked()) {
2765 if (location->is_mark()) {
2766 location->set_start ((*x)->start());
2768 location->set ((*x)->start(), (*x)->end());
2773 XMLNode &after = _editor->session()->locations()->get_state();
2774 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2775 _editor->commit_reversible_command ();
2779 MarkerDrag::aborted (bool)
2785 MarkerDrag::update_item (Location*)
2790 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2792 _cumulative_x_drag (0),
2793 _cumulative_y_drag (0)
2795 if (_zero_gain_fraction < 0.0) {
2796 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2799 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2801 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2807 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2809 Drag::start_grab (event, _editor->cursors()->fader);
2811 // start the grab at the center of the control point so
2812 // the point doesn't 'jump' to the mouse after the first drag
2813 _fixed_grab_x = _point->get_x();
2814 _fixed_grab_y = _point->get_y();
2816 float const fraction = 1 - (_point->get_y() / _point->line().height());
2818 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2820 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2821 event->button.x + 10, event->button.y + 10);
2823 _editor->verbose_cursor()->show ();
2825 if (!_point->can_slide ()) {
2826 _x_constrained = true;
2831 ControlPointDrag::motion (GdkEvent* event, bool)
2833 double dx = _drags->current_pointer_x() - last_pointer_x();
2834 double dy = _drags->current_pointer_y() - last_pointer_y();
2836 if (event->button.state & Keyboard::SecondaryModifier) {
2841 /* coordinate in pixels relative to the start of the region (for region-based automation)
2842 or track (for track-based automation) */
2843 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2844 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2846 // calculate zero crossing point. back off by .01 to stay on the
2847 // positive side of zero
2848 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2850 // make sure we hit zero when passing through
2851 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2855 if (_x_constrained) {
2858 if (_y_constrained) {
2862 _cumulative_x_drag = cx - _fixed_grab_x;
2863 _cumulative_y_drag = cy - _fixed_grab_y;
2867 cy = min ((double) _point->line().height(), cy);
2869 framepos_t cx_frames = _editor->unit_to_frame (cx);
2871 if (!_x_constrained) {
2872 _editor->snap_to_with_modifier (cx_frames, event);
2875 cx_frames = min (cx_frames, _point->line().maximum_time());
2877 float const fraction = 1.0 - (cy / _point->line().height());
2879 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2881 _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, push);
2883 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2887 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2889 if (!movement_occurred) {
2893 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2894 _editor->reset_point_selection ();
2898 motion (event, false);
2901 _point->line().end_drag ();
2902 _editor->session()->commit_reversible_command ();
2906 ControlPointDrag::aborted (bool)
2908 _point->line().reset ();
2912 ControlPointDrag::active (Editing::MouseMode m)
2914 if (m == Editing::MouseGain) {
2915 /* always active in mouse gain */
2919 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2920 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2923 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2926 _cumulative_y_drag (0)
2928 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2932 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2934 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2937 _item = &_line->grab_item ();
2939 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2940 origin, and ditto for y.
2943 double cx = event->button.x;
2944 double cy = event->button.y;
2946 _line->parent_group().w2i (cx, cy);
2948 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2953 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2954 /* no adjacent points */
2958 Drag::start_grab (event, _editor->cursors()->fader);
2960 /* store grab start in parent frame */
2965 double fraction = 1.0 - (cy / _line->height());
2967 _line->start_drag_line (before, after, fraction);
2969 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2970 event->button.x + 10, event->button.y + 10);
2972 _editor->verbose_cursor()->show ();
2976 LineDrag::motion (GdkEvent* event, bool)
2978 double dy = _drags->current_pointer_y() - last_pointer_y();
2980 if (event->button.state & Keyboard::SecondaryModifier) {
2984 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2986 _cumulative_y_drag = cy - _fixed_grab_y;
2989 cy = min ((double) _line->height(), cy);
2991 double const fraction = 1.0 - (cy / _line->height());
2992 bool const push = !Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2994 /* we are ignoring x position for this drag, so we can just pass in anything */
2995 _line->drag_motion (0, fraction, true, push);
2997 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3001 LineDrag::finished (GdkEvent* event, bool)
3003 motion (event, false);
3005 _editor->session()->commit_reversible_command ();
3009 LineDrag::aborted (bool)
3014 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3017 _cumulative_x_drag (0)
3019 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3023 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3025 Drag::start_grab (event);
3027 _line = reinterpret_cast<Line*> (_item);
3030 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3032 double cx = event->button.x;
3033 double cy = event->button.y;
3035 _item->property_parent().get_value()->w2i(cx, cy);
3037 /* store grab start in parent frame */
3038 _region_view_grab_x = cx;
3040 _before = *(float*) _item->get_data ("position");
3042 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3044 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3048 FeatureLineDrag::motion (GdkEvent*, bool)
3050 double dx = _drags->current_pointer_x() - last_pointer_x();
3052 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3054 _cumulative_x_drag += dx;
3056 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3065 ArdourCanvas::Points points;
3067 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3069 _line->get_bounds(x1, y2, x2, y2);
3071 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3072 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3074 _line->property_points() = points;
3076 float *pos = new float;
3079 _line->set_data ("position", pos);
3085 FeatureLineDrag::finished (GdkEvent*, bool)
3087 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3088 _arv->update_transient(_before, _before);
3092 FeatureLineDrag::aborted (bool)
3097 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3099 , _vertical_only (false)
3101 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3105 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3107 Drag::start_grab (event);
3108 show_verbose_cursor_time (adjusted_current_frame (event));
3112 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3119 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3121 framepos_t grab = grab_frame ();
3122 if (Config->get_rubberbanding_snaps_to_grid ()) {
3123 _editor->snap_to_with_modifier (grab, event);
3126 /* base start and end on initial click position */
3136 if (_drags->current_pointer_y() < grab_y()) {
3137 y1 = _drags->current_pointer_y();
3140 y2 = _drags->current_pointer_y();
3145 if (start != end || y1 != y2) {
3147 double x1 = _editor->frame_to_pixel (start);
3148 double x2 = _editor->frame_to_pixel (end);
3150 _editor->rubberband_rect->property_x1() = x1;
3151 if (_vertical_only) {
3152 /* fixed 10 pixel width */
3153 _editor->rubberband_rect->property_x2() = x1 + 10;
3155 _editor->rubberband_rect->property_x2() = x2;
3158 _editor->rubberband_rect->property_y1() = y1;
3159 _editor->rubberband_rect->property_y2() = y2;
3161 _editor->rubberband_rect->show();
3162 _editor->rubberband_rect->raise_to_top();
3164 show_verbose_cursor_time (pf);
3166 do_select_things (event, true);
3171 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3176 if (grab_frame() < last_pointer_frame()) {
3178 x2 = last_pointer_frame ();
3181 x1 = last_pointer_frame ();
3187 if (_drags->current_pointer_y() < grab_y()) {
3188 y1 = _drags->current_pointer_y();
3191 y2 = _drags->current_pointer_y();
3195 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3199 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3201 if (movement_occurred) {
3203 motion (event, false);
3204 do_select_things (event, false);
3210 bool do_deselect = true;
3211 MidiTimeAxisView* mtv;
3213 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3215 if (_editor->selection->empty()) {
3216 /* nothing selected */
3217 add_midi_region (mtv);
3218 do_deselect = false;
3228 _editor->rubberband_rect->hide();
3232 RubberbandSelectDrag::aborted (bool)
3234 _editor->rubberband_rect->hide ();
3237 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3238 : RegionDrag (e, i, p, v)
3240 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3244 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3246 Drag::start_grab (event, cursor);
3248 show_verbose_cursor_time (adjusted_current_frame (event));
3252 TimeFXDrag::motion (GdkEvent* event, bool)
3254 RegionView* rv = _primary;
3255 StreamView* cv = rv->get_time_axis_view().view ();
3257 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3258 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3259 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3261 framepos_t const pf = adjusted_current_frame (event);
3263 if (pf > rv->region()->position()) {
3264 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3267 show_verbose_cursor_time (pf);
3271 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3273 _primary->get_time_axis_view().hide_timestretch ();
3275 if (!movement_occurred) {
3279 if (last_pointer_frame() < _primary->region()->position()) {
3280 /* backwards drag of the left edge - not usable */
3284 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3286 float percentage = (double) newlen / (double) _primary->region()->length();
3288 #ifndef USE_RUBBERBAND
3289 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3290 if (_primary->region()->data_type() == DataType::AUDIO) {
3291 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3295 if (!_editor->get_selection().regions.empty()) {
3296 /* primary will already be included in the selection, and edit
3297 group shared editing will propagate selection across
3298 equivalent regions, so just use the current region
3302 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3303 error << _("An error occurred while executing time stretch operation") << endmsg;
3309 TimeFXDrag::aborted (bool)
3311 _primary->get_time_axis_view().hide_timestretch ();
3314 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3317 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3321 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3323 Drag::start_grab (event);
3327 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3329 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3333 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3335 if (movement_occurred && _editor->session()) {
3336 /* make sure we stop */
3337 _editor->session()->request_transport_speed (0.0);
3342 ScrubDrag::aborted (bool)
3347 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3351 , _original_pointer_time_axis (-1)
3352 , _last_pointer_time_axis (-1)
3353 , _time_selection_at_start (!_editor->get_selection().time.empty())
3355 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3359 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3361 if (_editor->session() == 0) {
3365 Gdk::Cursor* cursor = 0;
3367 switch (_operation) {
3368 case CreateSelection:
3369 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3374 cursor = _editor->cursors()->selector;
3375 Drag::start_grab (event, cursor);
3378 case SelectionStartTrim:
3379 if (_editor->clicked_axisview) {
3380 _editor->clicked_axisview->order_selection_trims (_item, true);
3382 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3385 case SelectionEndTrim:
3386 if (_editor->clicked_axisview) {
3387 _editor->clicked_axisview->order_selection_trims (_item, false);
3389 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3393 Drag::start_grab (event, cursor);
3397 if (_operation == SelectionMove) {
3398 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3400 show_verbose_cursor_time (adjusted_current_frame (event));
3403 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3407 SelectionDrag::setup_pointer_frame_offset ()
3409 switch (_operation) {
3410 case CreateSelection:
3411 _pointer_frame_offset = 0;
3414 case SelectionStartTrim:
3416 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3419 case SelectionEndTrim:
3420 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3426 SelectionDrag::motion (GdkEvent* event, bool first_move)
3428 framepos_t start = 0;
3432 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3433 if (pending_time_axis.first == 0) {
3437 framepos_t const pending_position = adjusted_current_frame (event);
3439 /* only alter selection if things have changed */
3441 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3445 switch (_operation) {
3446 case CreateSelection:
3448 framepos_t grab = grab_frame ();
3451 _editor->snap_to (grab);
3454 if (pending_position < grab_frame()) {
3455 start = pending_position;
3458 end = pending_position;
3462 /* first drag: Either add to the selection
3463 or create a new selection
3469 /* adding to the selection */
3470 _editor->set_selected_track_as_side_effect (Selection::Add);
3471 //_editor->selection->add (_editor->clicked_axisview);
3472 _editor->clicked_selection = _editor->selection->add (start, end);
3477 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3478 //_editor->selection->set (_editor->clicked_axisview);
3479 _editor->set_selected_track_as_side_effect (Selection::Set);
3482 _editor->clicked_selection = _editor->selection->set (start, end);
3486 /* select the track that we're in */
3487 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3488 // _editor->set_selected_track_as_side_effect (Selection::Add);
3489 _editor->selection->add (pending_time_axis.first);
3490 _added_time_axes.push_back (pending_time_axis.first);
3493 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3494 tracks that we selected in the first place.
3497 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3498 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3500 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3501 while (i != _added_time_axes.end()) {
3503 list<TimeAxisView*>::iterator tmp = i;
3506 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3507 _editor->selection->remove (*i);
3508 _added_time_axes.remove (*i);
3517 case SelectionStartTrim:
3519 start = _editor->selection->time[_editor->clicked_selection].start;
3520 end = _editor->selection->time[_editor->clicked_selection].end;
3522 if (pending_position > end) {
3525 start = pending_position;
3529 case SelectionEndTrim:
3531 start = _editor->selection->time[_editor->clicked_selection].start;
3532 end = _editor->selection->time[_editor->clicked_selection].end;
3534 if (pending_position < start) {
3537 end = pending_position;
3544 start = _editor->selection->time[_editor->clicked_selection].start;
3545 end = _editor->selection->time[_editor->clicked_selection].end;
3547 length = end - start;
3549 start = pending_position;
3550 _editor->snap_to (start);
3552 end = start + length;
3557 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3558 _editor->start_canvas_autoscroll (1, 0);
3562 _editor->selection->replace (_editor->clicked_selection, start, end);
3565 if (_operation == SelectionMove) {
3566 show_verbose_cursor_time(start);
3568 show_verbose_cursor_time(pending_position);
3573 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3575 Session* s = _editor->session();
3577 if (movement_occurred) {
3578 motion (event, false);
3579 /* XXX this is not object-oriented programming at all. ick */
3580 if (_editor->selection->time.consolidate()) {
3581 _editor->selection->TimeChanged ();
3584 /* XXX what if its a music time selection? */
3586 if ((s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3587 s->request_play_range (&_editor->selection->time, true);
3589 if (Config->get_always_play_range()) {
3590 if (_editor->doing_range_stuff()) {
3591 s->request_locate (_editor->get_selection().time.start());
3598 /* just a click, no pointer movement.
3601 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3602 if (!_time_selection_at_start) {
3603 if (_editor->clicked_regionview) {
3604 if (_editor->get_selection().selected (_editor->clicked_regionview)) {
3605 /* range select the entire current
3608 _editor->select_range (_editor->get_selection().regions.start(),
3609 _editor->get_selection().regions.end_frame());
3611 /* range select this (unselected)
3614 _editor->select_range (_editor->clicked_regionview->region()->position(),
3615 _editor->clicked_regionview->region()->last_frame());
3619 _editor->selection->clear_time();
3623 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3624 _editor->selection->set (_editor->clicked_axisview);
3627 if (s && s->get_play_range () && s->transport_rolling()) {
3628 s->request_stop (false, false);
3631 if (Config->get_always_play_range()) {
3632 if (_editor->doing_range_stuff()) {
3633 s->request_locate (_editor->get_selection().time.start());
3638 _editor->stop_canvas_autoscroll ();
3642 SelectionDrag::aborted (bool)
3647 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3652 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3654 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3655 physical_screen_height (_editor->get_window()));
3656 _drag_rect->hide ();
3658 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3659 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3663 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3665 if (_editor->session() == 0) {
3669 Gdk::Cursor* cursor = 0;
3671 if (!_editor->temp_location) {
3672 _editor->temp_location = new Location (*_editor->session());
3675 switch (_operation) {
3676 case CreateRangeMarker:
3677 case CreateTransportMarker:
3678 case CreateCDMarker:
3680 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3685 cursor = _editor->cursors()->selector;
3689 Drag::start_grab (event, cursor);
3691 show_verbose_cursor_time (adjusted_current_frame (event));
3695 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3697 framepos_t start = 0;
3699 ArdourCanvas::SimpleRect *crect;
3701 switch (_operation) {
3702 case CreateRangeMarker:
3703 crect = _editor->range_bar_drag_rect;
3705 case CreateTransportMarker:
3706 crect = _editor->transport_bar_drag_rect;
3708 case CreateCDMarker:
3709 crect = _editor->cd_marker_bar_drag_rect;
3712 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3717 framepos_t const pf = adjusted_current_frame (event);
3719 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3720 framepos_t grab = grab_frame ();
3721 _editor->snap_to (grab);
3723 if (pf < grab_frame()) {
3731 /* first drag: Either add to the selection
3732 or create a new selection.
3737 _editor->temp_location->set (start, end);
3741 update_item (_editor->temp_location);
3743 //_drag_rect->raise_to_top();
3748 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3749 _editor->start_canvas_autoscroll (1, 0);
3753 _editor->temp_location->set (start, end);
3755 double x1 = _editor->frame_to_pixel (start);
3756 double x2 = _editor->frame_to_pixel (end);
3757 crect->property_x1() = x1;
3758 crect->property_x2() = x2;
3760 update_item (_editor->temp_location);
3763 show_verbose_cursor_time (pf);
3768 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3770 Location * newloc = 0;
3774 if (movement_occurred) {
3775 motion (event, false);
3778 switch (_operation) {
3779 case CreateRangeMarker:
3780 case CreateCDMarker:
3782 _editor->begin_reversible_command (_("new range marker"));
3783 XMLNode &before = _editor->session()->locations()->get_state();
3784 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3785 if (_operation == CreateCDMarker) {
3786 flags = Location::IsRangeMarker | Location::IsCDMarker;
3787 _editor->cd_marker_bar_drag_rect->hide();
3790 flags = Location::IsRangeMarker;
3791 _editor->range_bar_drag_rect->hide();
3793 newloc = new Location (
3794 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3797 _editor->session()->locations()->add (newloc, true);
3798 XMLNode &after = _editor->session()->locations()->get_state();
3799 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3800 _editor->commit_reversible_command ();
3804 case CreateTransportMarker:
3805 // popup menu to pick loop or punch
3806 _editor->new_transport_marker_context_menu (&event->button, _item);
3810 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3812 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3817 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3819 if (end == max_framepos) {
3820 end = _editor->session()->current_end_frame ();
3823 if (start == max_framepos) {
3824 start = _editor->session()->current_start_frame ();
3827 switch (_editor->mouse_mode) {
3829 /* find the two markers on either side and then make the selection from it */
3830 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3834 /* find the two markers on either side of the click and make the range out of it */
3835 _editor->selection->set (start, end);
3844 _editor->stop_canvas_autoscroll ();
3848 RangeMarkerBarDrag::aborted (bool)
3854 RangeMarkerBarDrag::update_item (Location* location)
3856 double const x1 = _editor->frame_to_pixel (location->start());
3857 double const x2 = _editor->frame_to_pixel (location->end());
3859 _drag_rect->property_x1() = x1;
3860 _drag_rect->property_x2() = x2;
3863 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3867 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3871 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3873 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3874 Drag::start_grab (event, _editor->cursors()->zoom_out);
3877 Drag::start_grab (event, _editor->cursors()->zoom_in);
3881 show_verbose_cursor_time (adjusted_current_frame (event));
3885 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3890 framepos_t const pf = adjusted_current_frame (event);
3892 framepos_t grab = grab_frame ();
3893 _editor->snap_to_with_modifier (grab, event);
3895 /* base start and end on initial click position */
3907 _editor->zoom_rect->show();
3908 _editor->zoom_rect->raise_to_top();
3911 _editor->reposition_zoom_rect(start, end);
3913 show_verbose_cursor_time (pf);
3918 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3920 if (movement_occurred) {
3921 motion (event, false);
3923 if (grab_frame() < last_pointer_frame()) {
3924 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
3926 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
3929 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3930 _editor->tav_zoom_step (_zoom_out);
3932 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3936 _editor->zoom_rect->hide();
3940 MouseZoomDrag::aborted (bool)
3942 _editor->zoom_rect->hide ();
3945 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3947 , _cumulative_dx (0)
3948 , _cumulative_dy (0)
3950 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3952 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3953 _region = &_primary->region_view ();
3954 _note_height = _region->midi_stream_view()->note_height ();
3958 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3960 Drag::start_grab (event);
3962 if (!(_was_selected = _primary->selected())) {
3964 /* tertiary-click means extend selection - we'll do that on button release,
3965 so don't add it here, because otherwise we make it hard to figure
3966 out the "extend-to" range.
3969 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3972 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3975 _region->note_selected (_primary, true);
3977 _region->unique_select (_primary);
3983 /** @return Current total drag x change in frames */
3985 NoteDrag::total_dx () const
3988 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3990 /* primary note time */
3991 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3993 /* new time of the primary note in session frames */
3994 frameoffset_t st = n + dx;
3996 framepos_t const rp = _region->region()->position ();
3998 /* prevent the note being dragged earlier than the region's position */
4001 /* snap and return corresponding delta */
4002 return _region->snap_frame_to_frame (st - rp) + rp - n;
4005 /** @return Current total drag y change in note number */
4007 NoteDrag::total_dy () const
4009 MidiStreamView* msv = _region->midi_stream_view ();
4010 double const y = _region->midi_view()->y_position ();
4011 /* new current note */
4012 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4014 n = max (msv->lowest_note(), n);
4015 n = min (msv->highest_note(), n);
4016 /* and work out delta */
4017 return n - msv->y_to_note (grab_y() - y);
4021 NoteDrag::motion (GdkEvent *, bool)
4023 /* Total change in x and y since the start of the drag */
4024 frameoffset_t const dx = total_dx ();
4025 int8_t const dy = total_dy ();
4027 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4028 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4029 double const tdy = -dy * _note_height - _cumulative_dy;
4032 _cumulative_dx += tdx;
4033 _cumulative_dy += tdy;
4035 int8_t note_delta = total_dy();
4037 _region->move_selection (tdx, tdy, note_delta);
4039 /* the new note value may be the same as the old one, but we
4040 * don't know what that means because the selection may have
4041 * involved more than one note and we might be doing something
4042 * odd with them. so show the note value anyway, always.
4046 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4048 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4049 (int) floor (new_note));
4051 show_verbose_cursor_text (buf);
4056 NoteDrag::finished (GdkEvent* ev, bool moved)
4059 /* no motion - select note */
4061 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4062 _editor->current_mouse_mode() == Editing::MouseDraw) {
4064 if (_was_selected) {
4065 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4067 _region->note_deselected (_primary);
4070 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4071 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4073 if (!extend && !add && _region->selection_size() > 1) {
4074 _region->unique_select (_primary);
4075 } else if (extend) {
4076 _region->note_selected (_primary, true, true);
4078 /* it was added during button press */
4083 _region->note_dropped (_primary, total_dx(), total_dy());
4088 NoteDrag::aborted (bool)
4093 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4094 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4095 : Drag (editor, atv->base_item ())
4097 , _nothing_to_drag (false)
4099 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4101 setup (atv->lines ());
4104 /** Make an AutomationRangeDrag for region gain lines */
4105 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4106 : Drag (editor, rv->get_canvas_group ())
4108 , _nothing_to_drag (false)
4110 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4112 list<boost::shared_ptr<AutomationLine> > lines;
4113 lines.push_back (rv->get_gain_line ());
4117 /** @param lines AutomationLines to drag.
4118 * @param offset Offset from the session start to the points in the AutomationLines.
4121 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4123 /* find the lines that overlap the ranges being dragged */
4124 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4125 while (i != lines.end ()) {
4126 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4129 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4131 /* check this range against all the AudioRanges that we are using */
4132 list<AudioRange>::const_iterator k = _ranges.begin ();
4133 while (k != _ranges.end()) {
4134 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4140 /* add it to our list if it overlaps at all */
4141 if (k != _ranges.end()) {
4146 _lines.push_back (n);
4152 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4156 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4158 Drag::start_grab (event, cursor);
4160 /* Get line states before we start changing things */
4161 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4162 i->state = &i->line->get_state ();
4165 if (_ranges.empty()) {
4167 /* No selected time ranges: drag all points */
4168 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4169 uint32_t const N = i->line->npoints ();
4170 for (uint32_t j = 0; j < N; ++j) {
4171 i->points.push_back (i->line->nth (j));
4177 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4179 framecnt_t const half = (i->start + i->end) / 2;
4181 /* find the line that this audio range starts in */
4182 list<Line>::iterator j = _lines.begin();
4183 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4187 if (j != _lines.end()) {
4188 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4190 /* j is the line that this audio range starts in; fade into it;
4191 64 samples length plucked out of thin air.
4194 framepos_t a = i->start + 64;
4199 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4200 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4202 the_list->add (p, the_list->eval (p));
4203 the_list->add (q, the_list->eval (q));
4206 /* same thing for the end */
4209 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4213 if (j != _lines.end()) {
4214 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4216 /* j is the line that this audio range starts in; fade out of it;
4217 64 samples length plucked out of thin air.
4220 framepos_t b = i->end - 64;
4225 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4226 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4228 the_list->add (p, the_list->eval (p));
4229 the_list->add (q, the_list->eval (q));
4233 _nothing_to_drag = true;
4235 /* Find all the points that should be dragged and put them in the relevant
4236 points lists in the Line structs.
4239 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4241 uint32_t const N = i->line->npoints ();
4242 for (uint32_t j = 0; j < N; ++j) {
4244 /* here's a control point on this line */
4245 ControlPoint* p = i->line->nth (j);
4246 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4248 /* see if it's inside a range */
4249 list<AudioRange>::const_iterator k = _ranges.begin ();
4250 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4254 if (k != _ranges.end()) {
4255 /* dragging this point */
4256 _nothing_to_drag = false;
4257 i->points.push_back (p);
4263 if (_nothing_to_drag) {
4267 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4268 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4273 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4275 if (_nothing_to_drag) {
4279 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4280 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4282 /* we are ignoring x position for this drag, so we can just pass in anything */
4283 i->line->drag_motion (0, f, true, false);
4288 AutomationRangeDrag::finished (GdkEvent* event, bool)
4290 if (_nothing_to_drag) {
4294 motion (event, false);
4295 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4296 i->line->end_drag ();
4299 _editor->session()->commit_reversible_command ();
4303 AutomationRangeDrag::aborted (bool)
4305 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4310 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4313 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4314 layer = v->region()->layer ();
4315 initial_y = v->get_canvas_group()->property_y ();
4316 initial_playlist = v->region()->playlist ();
4317 initial_position = v->region()->position ();
4318 initial_end = v->region()->position () + v->region()->length ();
4321 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4325 , _cumulative_dx (0)
4327 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4328 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4333 PatchChangeDrag::motion (GdkEvent* ev, bool)
4335 framepos_t f = adjusted_current_frame (ev);
4336 boost::shared_ptr<Region> r = _region_view->region ();
4337 f = max (f, r->position ());
4338 f = min (f, r->last_frame ());
4340 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4341 double const dxu = _editor->frame_to_unit (dxf); // permitted fx in units
4342 _patch_change->move (dxu - _cumulative_dx, 0);
4343 _cumulative_dx = dxu;
4347 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4349 if (!movement_occurred) {
4353 boost::shared_ptr<Region> r (_region_view->region ());
4354 framepos_t f = adjusted_current_frame (ev);
4355 f = max (f, r->position ());
4356 f = min (f, r->last_frame ());
4358 _region_view->move_patch_change (
4360 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4365 PatchChangeDrag::aborted (bool)
4367 _patch_change->move (-_cumulative_dx, 0);
4371 PatchChangeDrag::setup_pointer_frame_offset ()
4373 boost::shared_ptr<Region> region = _region_view->region ();
4374 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4377 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4378 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4385 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4387 framepos_t const p = _region_view->region()->position ();
4388 double const y = _region_view->midi_view()->y_position ();
4390 x1 = max ((framepos_t) 0, x1 - p);
4391 x2 = max ((framepos_t) 0, x2 - p);
4392 y1 = max (0.0, y1 - y);
4393 y2 = max (0.0, y2 - y);
4395 _region_view->update_drag_selection (
4396 _editor->frame_to_pixel (x1),
4397 _editor->frame_to_pixel (x2),
4400 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4405 MidiRubberbandSelectDrag::deselect_things ()
4410 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4411 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4414 _vertical_only = true;
4418 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4420 double const y = _region_view->midi_view()->y_position ();
4422 y1 = max (0.0, y1 - y);
4423 y2 = max (0.0, y2 - y);
4425 _region_view->update_vertical_drag_selection (
4428 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4433 MidiVerticalSelectDrag::deselect_things ()
4438 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4439 : RubberbandSelectDrag (e, i)
4445 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4447 if (drag_in_progress) {
4448 /* We just want to select things at the end of the drag, not during it */
4452 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4454 _editor->begin_reversible_command (_("rubberband selection"));
4455 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4456 _editor->commit_reversible_command ();
4460 EditorRubberbandSelectDrag::deselect_things ()
4462 if (!getenv("ARDOUR_SAE")) {
4463 _editor->selection->clear_tracks();
4465 _editor->selection->clear_regions();
4466 _editor->selection->clear_points ();
4467 _editor->selection->clear_lines ();
4470 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4478 NoteCreateDrag::~NoteCreateDrag ()
4484 NoteCreateDrag::grid_frames (framepos_t t) const
4487 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4492 return _region_view->region_beats_to_region_frames (grid_beats);
4496 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4498 Drag::start_grab (event, cursor);
4500 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4502 framepos_t pf = _drags->current_pointer_frame ();
4503 framecnt_t const g = grid_frames (pf);
4505 /* Hack so that we always snap to the note that we are over, instead of snapping
4506 to the next one if we're more than halfway through the one we're over.
4508 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4512 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4514 MidiStreamView* sv = _region_view->midi_stream_view ();
4515 double const x = _editor->frame_to_pixel (_note[0]);
4516 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4518 _drag_rect->property_x1() = x;
4519 _drag_rect->property_y1() = y;
4520 _drag_rect->property_x2() = x;
4521 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4523 _drag_rect->property_outline_what() = 0xff;
4524 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4525 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4529 NoteCreateDrag::motion (GdkEvent* event, bool)
4531 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4532 double const x = _editor->frame_to_pixel (_note[1]);
4533 if (_note[1] > _note[0]) {
4534 _drag_rect->property_x2() = x;
4536 _drag_rect->property_x1() = x;
4541 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4543 if (!had_movement) {
4547 framepos_t const start = min (_note[0], _note[1]);
4548 framecnt_t length = abs (_note[0] - _note[1]);
4550 framecnt_t const g = grid_frames (start);
4551 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4553 if (_editor->snap_mode() == SnapNormal && length < g) {
4554 length = g - one_tick;
4557 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4559 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4563 NoteCreateDrag::y_to_region (double y) const
4566 _region_view->get_canvas_group()->w2i (x, y);
4571 NoteCreateDrag::aborted (bool)
4576 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4584 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4586 Drag::start_grab (event, cursor);
4590 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4596 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4599 distance = _drags->current_pointer_x() - grab_x();
4600 len = ar->fade_in()->back()->when;
4602 distance = grab_x() - _drags->current_pointer_x();
4603 len = ar->fade_out()->back()->when;
4606 /* how long should it be ? */
4608 new_length = len + _editor->unit_to_frame (distance);
4610 /* now check with the region that this is legal */
4612 new_length = ar->verify_xfade_bounds (new_length, start);
4615 arv->redraw_start_xfade_to (ar, new_length);
4617 arv->redraw_end_xfade_to (ar, new_length);
4622 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4628 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4631 distance = _drags->current_pointer_x() - grab_x();
4632 len = ar->fade_in()->back()->when;
4634 distance = grab_x() - _drags->current_pointer_x();
4635 len = ar->fade_out()->back()->when;
4638 new_length = ar->verify_xfade_bounds (len + _editor->unit_to_frame (distance), start);
4640 _editor->begin_reversible_command ("xfade trim");
4641 ar->playlist()->clear_owned_changes ();
4644 ar->set_fade_in_length (new_length);
4646 ar->set_fade_out_length (new_length);
4649 /* Adjusting the xfade may affect other regions in the playlist, so we need
4650 to get undo Commands from the whole playlist rather than just the
4654 vector<Command*> cmds;
4655 ar->playlist()->rdiff (cmds);
4656 _editor->session()->add_commands (cmds);
4657 _editor->commit_reversible_command ();
4662 CrossfadeEdgeDrag::aborted (bool)
4665 arv->redraw_start_xfade ();
4667 arv->redraw_end_xfade ();