2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/audioregion.h"
33 #include "ardour/dB.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/operations.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/session.h"
42 #include "audio_region_view.h"
43 #include "midi_region_view.h"
44 #include "ardour_ui.h"
45 #include "gui_thread.h"
46 #include "control_point.h"
48 #include "region_gain_line.h"
49 #include "editor_drag.h"
50 #include "audio_time_axis.h"
51 #include "midi_time_axis.h"
52 #include "canvas-note.h"
53 #include "selection.h"
54 #include "midi_selection.h"
55 #include "automation_time_axis.h"
57 #include "editor_cursors.h"
58 #include "mouse_cursors.h"
59 #include "verbose_cursor.h"
62 using namespace ARDOUR;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67 using namespace ArdourCanvas;
69 using Gtkmm2ext::Keyboard;
71 double ControlPointDrag::_zero_gain_fraction = -1.0;
73 DragManager::DragManager (Editor* e)
76 , _current_pointer_frame (0)
80 DragManager::~DragManager ()
85 /** Call abort for each active drag */
91 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
96 if (!_drags.empty ()) {
97 _editor->set_follow_playhead (_old_follow_playhead, false);
106 DragManager::add (Drag* d)
108 d->set_manager (this);
109 _drags.push_back (d);
113 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
115 d->set_manager (this);
116 _drags.push_back (d);
121 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
123 /* Prevent follow playhead during the drag to be nice to the user */
124 _old_follow_playhead = _editor->follow_playhead ();
125 _editor->set_follow_playhead (false);
127 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
129 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
130 (*i)->start_grab (e, c);
134 /** Call end_grab for each active drag.
135 * @return true if any drag reported movement having occurred.
138 DragManager::end_grab (GdkEvent* e)
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->end_grab (e);
155 _editor->set_follow_playhead (_old_follow_playhead, false);
161 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
165 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
167 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
168 bool const t = (*i)->motion_handler (e, from_autoscroll);
179 DragManager::have_item (ArdourCanvas::Item* i) const
181 list<Drag*>::const_iterator j = _drags.begin ();
182 while (j != _drags.end() && (*j)->item () != i) {
186 return j != _drags.end ();
189 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
192 , _pointer_frame_offset (0)
193 , _move_threshold_passed (false)
194 , _raw_grab_frame (0)
196 , _last_pointer_frame (0)
202 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
208 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time);
210 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
215 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
217 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
219 if (Keyboard::is_button2_event (&event->button)) {
220 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
221 _y_constrained = true;
222 _x_constrained = false;
224 _y_constrained = false;
225 _x_constrained = true;
228 _x_constrained = false;
229 _y_constrained = false;
232 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
233 setup_pointer_frame_offset ();
234 _grab_frame = adjusted_frame (_raw_grab_frame, event);
235 _last_pointer_frame = _grab_frame;
236 _last_pointer_x = _grab_x;
237 _last_pointer_y = _grab_y;
240 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
243 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
248 if (_editor->session() && _editor->session()->transport_rolling()) {
251 _was_rolling = false;
254 switch (_editor->snap_type()) {
255 case SnapToRegionStart:
256 case SnapToRegionEnd:
257 case SnapToRegionSync:
258 case SnapToRegionBoundary:
259 _editor->build_region_boundary_cache ();
266 /** Call to end a drag `successfully'. Ungrabs item and calls
267 * subclass' finished() method.
269 * @param event GDK event, or 0.
270 * @return true if some movement occurred, otherwise false.
273 Drag::end_grab (GdkEvent* event)
275 _editor->stop_canvas_autoscroll ();
277 _item->ungrab (event ? event->button.time : 0);
279 finished (event, _move_threshold_passed);
281 _editor->verbose_cursor()->hide ();
283 return _move_threshold_passed;
287 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
291 if (f > _pointer_frame_offset) {
292 pos = f - _pointer_frame_offset;
296 _editor->snap_to_with_modifier (pos, event);
303 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
305 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
309 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
311 /* check to see if we have moved in any way that matters since the last motion event */
312 if (_move_threshold_passed &&
313 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
314 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
318 pair<framecnt_t, int> const threshold = move_threshold ();
320 bool const old_move_threshold_passed = _move_threshold_passed;
322 if (!from_autoscroll && !_move_threshold_passed) {
324 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
325 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
327 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
330 if (active (_editor->mouse_mode) && _move_threshold_passed) {
332 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
333 if (!from_autoscroll) {
334 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
335 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
336 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
339 motion (event, _move_threshold_passed != old_move_threshold_passed);
341 _last_pointer_x = _drags->current_pointer_x ();
342 _last_pointer_y = _drags->current_pointer_y ();
343 _last_pointer_frame = adjusted_current_frame (event);
351 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
359 aborted (_move_threshold_passed);
361 _editor->stop_canvas_autoscroll ();
362 _editor->verbose_cursor()->hide ();
366 Drag::show_verbose_cursor_time (framepos_t frame)
368 _editor->verbose_cursor()->set_time (
370 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
371 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
374 _editor->verbose_cursor()->show ();
378 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
380 _editor->verbose_cursor()->show (xoffset);
382 _editor->verbose_cursor()->set_duration (
384 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
385 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
390 Drag::show_verbose_cursor_text (string const & text)
392 _editor->verbose_cursor()->show ();
394 _editor->verbose_cursor()->set (
396 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
397 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
401 boost::shared_ptr<Region>
402 Drag::add_midi_region (MidiTimeAxisView* view)
404 if (_editor->session()) {
405 const TempoMap& map (_editor->session()->tempo_map());
406 framecnt_t pos = grab_frame();
407 const Meter& m = map.meter_at (pos);
408 /* not that the frame rate used here can be affected by pull up/down which
411 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
412 return view->add_region (grab_frame(), len, true);
415 return boost::shared_ptr<Region>();
418 struct EditorOrderTimeAxisViewSorter {
419 bool operator() (TimeAxisView* a, TimeAxisView* b) {
420 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
421 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
423 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
427 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
431 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
433 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
434 as some of the regions we are dragging may be on such tracks.
437 TrackViewList track_views = _editor->track_views;
438 track_views.sort (EditorOrderTimeAxisViewSorter ());
440 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
441 _time_axis_views.push_back (*i);
443 TimeAxisView::Children children_list = (*i)->get_child_list ();
444 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
445 _time_axis_views.push_back (j->get());
449 /* the list of views can be empty at this point if this is a region list-insert drag
452 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
453 _views.push_back (DraggingView (*i, this));
456 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
460 RegionDrag::region_going_away (RegionView* v)
462 list<DraggingView>::iterator i = _views.begin ();
463 while (i != _views.end() && i->view != v) {
467 if (i != _views.end()) {
472 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
473 * or -1 if it is not found.
476 RegionDrag::find_time_axis_view (TimeAxisView* t) const
479 int const N = _time_axis_views.size ();
480 while (i < N && _time_axis_views[i] != t) {
491 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
492 : RegionDrag (e, i, p, v),
501 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
503 Drag::start_grab (event, cursor);
505 show_verbose_cursor_time (_last_frame_position);
507 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
508 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
509 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
513 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
515 /* compute the amount of pointer motion in frames, and where
516 the region would be if we moved it by that much.
518 *pending_region_position = adjusted_current_frame (event);
520 framepos_t sync_frame;
521 framecnt_t sync_offset;
524 sync_offset = _primary->region()->sync_offset (sync_dir);
526 /* we don't handle a sync point that lies before zero.
528 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
530 sync_frame = *pending_region_position + (sync_dir*sync_offset);
532 _editor->snap_to_with_modifier (sync_frame, event);
534 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
537 *pending_region_position = _last_frame_position;
540 if (*pending_region_position > max_framepos - _primary->region()->length()) {
541 *pending_region_position = _last_frame_position;
546 /* in locked edit mode, reverse the usual meaning of _x_constrained */
547 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
549 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
551 /* x movement since last time (in pixels) */
552 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
554 /* total x movement */
555 framecnt_t total_dx = *pending_region_position;
556 if (regions_came_from_canvas()) {
557 total_dx = total_dx - grab_frame ();
560 /* check that no regions have gone off the start of the session */
561 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
562 if ((i->view->region()->position() + total_dx) < 0) {
564 *pending_region_position = _last_frame_position;
569 _last_frame_position = *pending_region_position;
576 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
578 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
579 int const n = i->time_axis_view + delta_track;
580 if (n < 0 || n >= int (_time_axis_views.size())) {
581 /* off the top or bottom track */
585 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
586 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
587 /* not a track, or the wrong type */
591 double const l = i->layer + delta_layer;
593 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
594 mode to allow the user to place a region below another on layer 0.
596 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
597 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
598 If it has, the layers will be munged later anyway, so it's ok.
604 /* all regions being dragged are ok with this change */
609 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
611 assert (!_views.empty ());
613 /* Find the TimeAxisView that the pointer is now over */
614 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
616 if (first_move && tv.first->view()->layer_display() == Stacked) {
617 tv.first->view()->set_layer_display (Expanded);
620 /* Bail early if we're not over a track */
621 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
622 if (!rtv || !rtv->is_track()) {
623 _editor->verbose_cursor()->hide ();
627 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
629 /* Here's the current pointer position in terms of time axis view and layer */
630 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
631 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
633 /* Work out the change in x */
634 framepos_t pending_region_position;
635 double const x_delta = compute_x_delta (event, &pending_region_position);
637 /* Work out the change in y */
638 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
639 double delta_layer = current_pointer_layer - _last_pointer_layer;
641 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
642 /* this y movement is not allowed, so do no y movement this time */
643 delta_time_axis_view = 0;
647 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
648 /* haven't reached next snap point, and we're not switching
649 trackviews nor layers. nothing to do.
654 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
656 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
658 RegionView* rv = i->view;
660 if (rv->region()->locked()) {
666 rv->get_time_axis_view().hide_dependent_views (*rv);
668 /* Absolutely no idea why this is necessary, but it is; without
669 it, the region view disappears after the reparent.
671 _editor->update_canvas_now ();
673 /* Reparent to a non scrolling group so that we can keep the
674 region selection above all time axis views.
675 Reparenting means that we will have to move the region view
676 later, as the two parent groups have different coordinates.
679 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
681 rv->fake_set_opaque (true);
684 /* If we have moved tracks, we'll fudge the layer delta so that the
685 region gets moved back onto layer 0 on its new track; this avoids
686 confusion when dragging regions from non-zero layers onto different
689 double this_delta_layer = delta_layer;
690 if (delta_time_axis_view != 0) {
691 this_delta_layer = - i->layer;
694 /* The TimeAxisView that this region is now on */
695 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
697 /* Ensure it is moved from stacked -> expanded if appropriate */
698 if (tv->view()->layer_display() == Stacked) {
699 tv->view()->set_layer_display (Expanded);
702 /* We're only allowed to go -ve in layer on Expanded views */
703 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
704 this_delta_layer = - i->layer;
708 rv->set_height (tv->view()->child_height ());
710 /* Update show/hidden status as the region view may have come from a hidden track,
711 or have moved to one.
714 rv->get_canvas_group()->hide ();
716 rv->get_canvas_group()->show ();
719 /* Update the DraggingView */
720 i->time_axis_view += delta_time_axis_view;
721 i->layer += this_delta_layer;
724 _editor->mouse_brush_insert_region (rv, pending_region_position);
729 /* Get the y coordinate of the top of the track that this region is now on */
730 tv->canvas_display()->i2w (x, y);
731 y += _editor->get_trackview_group_vertical_offset();
733 /* And adjust for the layer that it should be on */
734 StreamView* cv = tv->view ();
735 switch (cv->layer_display ()) {
739 y += (cv->layers() - i->layer - 1) * cv->child_height ();
742 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
746 /* Now move the region view */
747 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
750 } /* foreach region */
752 _total_x_delta += x_delta;
755 _editor->cursor_group->raise_to_top();
758 if (x_delta != 0 && !_brushing) {
759 show_verbose_cursor_time (_last_frame_position);
762 _last_pointer_time_axis_view += delta_time_axis_view;
763 _last_pointer_layer += delta_layer;
767 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
769 if (_copy && first_move) {
771 /* duplicate the regionview(s) and region(s) */
773 list<DraggingView> new_regionviews;
775 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
777 RegionView* rv = i->view;
778 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
779 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
781 const boost::shared_ptr<const Region> original = rv->region();
782 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
783 region_copy->set_position (original->position());
787 boost::shared_ptr<AudioRegion> audioregion_copy
788 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
790 nrv = new AudioRegionView (*arv, audioregion_copy);
792 boost::shared_ptr<MidiRegion> midiregion_copy
793 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
794 nrv = new MidiRegionView (*mrv, midiregion_copy);
799 nrv->get_canvas_group()->show ();
800 new_regionviews.push_back (DraggingView (nrv, this));
802 /* swap _primary to the copy */
804 if (rv == _primary) {
808 /* ..and deselect the one we copied */
810 rv->set_selected (false);
813 if (!new_regionviews.empty()) {
815 /* reflect the fact that we are dragging the copies */
817 _views = new_regionviews;
819 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
822 sync the canvas to what we think is its current state
823 without it, the canvas seems to
824 "forget" to update properly after the upcoming reparent()
825 ..only if the mouse is in rapid motion at the time of the grab.
826 something to do with regionview creation taking so long?
828 _editor->update_canvas_now();
832 RegionMotionDrag::motion (event, first_move);
836 RegionMotionDrag::finished (GdkEvent *, bool)
838 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
843 if ((*i)->view()->layer_display() == Expanded) {
844 (*i)->view()->set_layer_display (Stacked);
850 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
852 RegionMotionDrag::finished (ev, movement_occurred);
854 if (!movement_occurred) {
859 /* reverse this here so that we have the correct logic to finalize
863 if (Config->get_edit_mode() == Lock) {
864 _x_constrained = !_x_constrained;
867 assert (!_views.empty ());
869 /* We might have hidden region views so that they weren't visible during the drag
870 (when they have been reparented). Now everything can be shown again, as region
871 views are back in their track parent groups.
873 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
874 i->view->get_canvas_group()->show ();
877 bool const changed_position = (_last_frame_position != _primary->region()->position());
878 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
879 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
881 _editor->update_canvas_now ();
901 if (_editor->session() && Config->get_always_play_range()) {
902 _editor->session()->request_locate (_editor->get_selection().regions.start());
907 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
909 RegionSelection new_views;
910 PlaylistSet modified_playlists;
911 list<RegionView*> views_to_delete;
914 /* all changes were made during motion event handlers */
916 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
920 _editor->commit_reversible_command ();
924 if (_x_constrained) {
925 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
927 _editor->begin_reversible_command (Operations::region_copy);
930 /* insert the regions into their new playlists */
931 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
933 if (i->view->region()->locked()) {
939 if (changed_position && !_x_constrained) {
940 where = i->view->region()->position() - drag_delta;
942 where = i->view->region()->position();
945 RegionView* new_view = insert_region_into_playlist (
946 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
953 new_views.push_back (new_view);
955 /* we don't need the copied RegionView any more */
956 views_to_delete.push_back (i->view);
959 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
960 because when views are deleted they are automagically removed from _views, which messes
963 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
967 /* If we've created new regions either by copying or moving
968 to a new track, we want to replace the old selection with the new ones
971 if (new_views.size() > 0) {
972 _editor->selection->set (new_views);
975 /* write commands for the accumulated diffs for all our modified playlists */
976 add_stateful_diff_commands_for_playlists (modified_playlists);
978 _editor->commit_reversible_command ();
982 RegionMoveDrag::finished_no_copy (
983 bool const changed_position,
984 bool const changed_tracks,
985 framecnt_t const drag_delta
988 RegionSelection new_views;
989 PlaylistSet modified_playlists;
990 PlaylistSet frozen_playlists;
991 set<RouteTimeAxisView*> views_to_update;
994 /* all changes were made during motion event handlers */
995 _editor->commit_reversible_command ();
999 if (_x_constrained) {
1000 _editor->begin_reversible_command (_("fixed time region drag"));
1002 _editor->begin_reversible_command (Operations::region_drag);
1005 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1007 RegionView* rv = i->view;
1009 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1010 double const dest_layer = i->layer;
1012 if (rv->region()->locked()) {
1017 views_to_update.insert (dest_rtv);
1021 if (changed_position && !_x_constrained) {
1022 where = rv->region()->position() - drag_delta;
1024 where = rv->region()->position();
1027 if (changed_tracks) {
1029 /* insert into new playlist */
1031 RegionView* new_view = insert_region_into_playlist (
1032 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1035 if (new_view == 0) {
1040 new_views.push_back (new_view);
1042 /* remove from old playlist */
1044 /* the region that used to be in the old playlist is not
1045 moved to the new one - we use a copy of it. as a result,
1046 any existing editor for the region should no longer be
1049 rv->hide_region_editor();
1050 rv->fake_set_opaque (false);
1052 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1056 rv->region()->clear_changes ();
1059 motion on the same track. plonk the previously reparented region
1060 back to its original canvas group (its streamview).
1061 No need to do anything for copies as they are fake regions which will be deleted.
1064 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1065 rv->get_canvas_group()->property_y() = i->initial_y;
1066 rv->get_time_axis_view().reveal_dependent_views (*rv);
1068 /* just change the model */
1070 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1072 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1073 playlist->set_layer (rv->region(), dest_layer);
1076 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1078 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1081 playlist->freeze ();
1084 /* this movement may result in a crossfade being modified, so we need to get undo
1085 data from the playlist as well as the region.
1088 r = modified_playlists.insert (playlist);
1090 playlist->clear_changes ();
1093 rv->region()->set_position (where);
1095 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1098 if (changed_tracks) {
1100 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1101 was selected in all of them, then removing it from a playlist will have removed all
1102 trace of it from _views (i.e. there were N regions selected, we removed 1,
1103 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1104 corresponding regionview, and _views is now empty).
1106 This could have invalidated any and all iterators into _views.
1108 The heuristic we use here is: if the region selection is empty, break out of the loop
1109 here. if the region selection is not empty, then restart the loop because we know that
1110 we must have removed at least the region(view) we've just been working on as well as any
1111 that we processed on previous iterations.
1113 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1114 we can just iterate.
1118 if (_views.empty()) {
1129 /* If we've created new regions either by copying or moving
1130 to a new track, we want to replace the old selection with the new ones
1133 if (new_views.size() > 0) {
1134 _editor->selection->set (new_views);
1137 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1141 /* write commands for the accumulated diffs for all our modified playlists */
1142 add_stateful_diff_commands_for_playlists (modified_playlists);
1144 _editor->commit_reversible_command ();
1146 /* We have futzed with the layering of canvas items on our streamviews.
1147 If any region changed layer, this will have resulted in the stream
1148 views being asked to set up their region views, and all will be well.
1149 If not, we might now have badly-ordered region views. Ask the StreamViews
1150 involved to sort themselves out, just in case.
1153 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1154 (*i)->view()->playlist_layered ((*i)->track ());
1158 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1159 * @param region Region to remove.
1160 * @param playlist playlist To remove from.
1161 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1162 * that clear_changes () is only called once per playlist.
1165 RegionMoveDrag::remove_region_from_playlist (
1166 boost::shared_ptr<Region> region,
1167 boost::shared_ptr<Playlist> playlist,
1168 PlaylistSet& modified_playlists
1171 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1174 playlist->clear_changes ();
1177 playlist->remove_region (region);
1181 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1182 * clearing the playlist's diff history first if necessary.
1183 * @param region Region to insert.
1184 * @param dest_rtv Destination RouteTimeAxisView.
1185 * @param dest_layer Destination layer.
1186 * @param where Destination position.
1187 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1188 * that clear_changes () is only called once per playlist.
1189 * @return New RegionView, or 0 if no insert was performed.
1192 RegionMoveDrag::insert_region_into_playlist (
1193 boost::shared_ptr<Region> region,
1194 RouteTimeAxisView* dest_rtv,
1197 PlaylistSet& modified_playlists
1200 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1201 if (!dest_playlist) {
1205 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1206 _new_region_view = 0;
1207 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1209 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1210 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1212 dest_playlist->clear_changes ();
1215 dest_playlist->add_region (region, where);
1217 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1218 dest_playlist->set_layer (region, dest_layer);
1223 assert (_new_region_view);
1225 return _new_region_view;
1229 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1231 _new_region_view = rv;
1235 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1237 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1238 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1240 _editor->session()->add_command (c);
1249 RegionMoveDrag::aborted (bool movement_occurred)
1253 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1260 RegionMotionDrag::aborted (movement_occurred);
1265 RegionMotionDrag::aborted (bool)
1267 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1268 if ((*i)->view()->layer_display() == Expanded) {
1269 (*i)->view()->set_layer_display (Stacked);
1273 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1274 RegionView* rv = i->view;
1275 TimeAxisView* tv = &(rv->get_time_axis_view ());
1276 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1278 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1279 rv->get_canvas_group()->property_y() = 0;
1280 rv->get_time_axis_view().reveal_dependent_views (*rv);
1281 rv->fake_set_opaque (false);
1282 rv->move (-_total_x_delta, 0);
1283 rv->set_height (rtv->view()->child_height ());
1286 _editor->update_canvas_now ();
1289 /** @param b true to brush, otherwise false.
1290 * @param c true to make copies of the regions being moved, otherwise false.
1292 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1293 : RegionMotionDrag (e, i, p, v, b),
1296 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1299 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1300 if (rtv && rtv->is_track()) {
1301 speed = rtv->track()->speed ();
1304 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1308 RegionMoveDrag::setup_pointer_frame_offset ()
1310 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1313 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1314 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1316 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1318 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1319 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1321 _primary = v->view()->create_region_view (r, false, false);
1323 _primary->get_canvas_group()->show ();
1324 _primary->set_position (pos, 0);
1325 _views.push_back (DraggingView (_primary, this));
1327 _last_frame_position = pos;
1329 _item = _primary->get_canvas_group ();
1333 RegionInsertDrag::finished (GdkEvent *, bool)
1335 _editor->update_canvas_now ();
1337 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1339 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1340 _primary->get_canvas_group()->property_y() = 0;
1342 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1344 _editor->begin_reversible_command (Operations::insert_region);
1345 playlist->clear_changes ();
1346 playlist->add_region (_primary->region (), _last_frame_position);
1347 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1348 _editor->commit_reversible_command ();
1356 RegionInsertDrag::aborted (bool)
1363 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1364 : RegionMoveDrag (e, i, p, v, false, false)
1366 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1369 struct RegionSelectionByPosition {
1370 bool operator() (RegionView*a, RegionView* b) {
1371 return a->region()->position () < b->region()->position();
1376 RegionSpliceDrag::motion (GdkEvent* event, bool)
1378 /* Which trackview is this ? */
1380 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1381 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1383 /* The region motion is only processed if the pointer is over
1387 if (!tv || !tv->is_track()) {
1388 /* To make sure we hide the verbose canvas cursor when the mouse is
1389 not held over and audiotrack.
1391 _editor->verbose_cursor()->hide ();
1397 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1403 RegionSelection copy (_editor->selection->regions);
1405 RegionSelectionByPosition cmp;
1408 framepos_t const pf = adjusted_current_frame (event);
1410 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1412 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1418 boost::shared_ptr<Playlist> playlist;
1420 if ((playlist = atv->playlist()) == 0) {
1424 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1429 if (pf < (*i)->region()->last_frame() + 1) {
1433 if (pf > (*i)->region()->first_frame()) {
1439 playlist->shuffle ((*i)->region(), dir);
1444 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1446 RegionMoveDrag::finished (event, movement_occurred);
1450 RegionSpliceDrag::aborted (bool)
1455 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1457 _view (dynamic_cast<MidiTimeAxisView*> (v))
1459 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1465 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1468 _region = add_midi_region (_view);
1469 _view->playlist()->freeze ();
1472 framepos_t const f = adjusted_current_frame (event);
1473 if (f < grab_frame()) {
1474 _region->set_position (f);
1477 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1478 so that if this region is duplicated, its duplicate starts on
1479 a snap point rather than 1 frame after a snap point. Otherwise things get
1480 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1481 place snapped notes at the start of the region.
1484 framecnt_t const len = abs (f - grab_frame () - 1);
1485 _region->set_length (len < 1 ? 1 : len);
1491 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1493 if (!movement_occurred) {
1494 add_midi_region (_view);
1496 _view->playlist()->thaw ();
1500 _editor->commit_reversible_command ();
1505 RegionCreateDrag::aborted (bool)
1508 _view->playlist()->thaw ();
1514 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1518 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1522 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1524 Gdk::Cursor* cursor;
1525 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1526 float x_fraction = cnote->mouse_x_fraction ();
1528 if (x_fraction > 0.0 && x_fraction < 0.25) {
1529 cursor = _editor->cursors()->left_side_trim;
1531 cursor = _editor->cursors()->right_side_trim;
1534 Drag::start_grab (event, cursor);
1536 region = &cnote->region_view();
1538 double const region_start = region->get_position_pixels();
1539 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1541 if (grab_x() <= middle_point) {
1542 cursor = _editor->cursors()->left_side_trim;
1545 cursor = _editor->cursors()->right_side_trim;
1549 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1551 if (event->motion.state & Keyboard::PrimaryModifier) {
1557 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1559 if (ms.size() > 1) {
1560 /* has to be relative, may make no sense otherwise */
1564 /* select this note; if it is already selected, preserve the existing selection,
1565 otherwise make this note the only one selected.
1567 region->note_selected (cnote, cnote->selected ());
1569 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1570 MidiRegionSelection::iterator next;
1573 (*r)->begin_resizing (at_front);
1579 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1581 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1582 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1583 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1588 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1590 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1591 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1592 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1597 NoteResizeDrag::aborted (bool)
1599 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1600 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1601 (*r)->abort_resizing ();
1605 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1606 : RegionDrag (e, i, p, v)
1608 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1612 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1615 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1616 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1618 if (tv && tv->is_track()) {
1619 speed = tv->track()->speed();
1622 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1623 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1624 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1626 framepos_t const pf = adjusted_current_frame (event);
1628 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1629 /* Move the contents of the region around without changing the region bounds */
1630 _operation = ContentsTrim;
1631 Drag::start_grab (event, _editor->cursors()->trimmer);
1633 /* These will get overridden for a point trim.*/
1634 if (pf < (region_start + region_length/2)) {
1635 /* closer to front */
1636 _operation = StartTrim;
1637 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1640 _operation = EndTrim;
1641 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1645 switch (_operation) {
1647 show_verbose_cursor_time (region_start);
1648 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1649 i->view->trim_front_starting ();
1653 show_verbose_cursor_time (region_end);
1656 show_verbose_cursor_time (pf);
1660 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1661 i->view->region()->suspend_property_changes ();
1666 TrimDrag::motion (GdkEvent* event, bool first_move)
1668 RegionView* rv = _primary;
1671 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1672 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1673 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1675 if (tv && tv->is_track()) {
1676 speed = tv->track()->speed();
1679 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1685 switch (_operation) {
1687 trim_type = "Region start trim";
1690 trim_type = "Region end trim";
1693 trim_type = "Region content trim";
1697 _editor->begin_reversible_command (trim_type);
1699 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1700 RegionView* rv = i->view;
1701 rv->fake_set_opaque (false);
1702 rv->enable_display (false);
1703 rv->region()->playlist()->clear_owned_changes ();
1705 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1708 arv->temporarily_hide_envelope ();
1711 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1712 insert_result = _editor->motion_frozen_playlists.insert (pl);
1714 if (insert_result.second) {
1720 bool non_overlap_trim = false;
1722 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1723 non_overlap_trim = true;
1726 switch (_operation) {
1728 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1729 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1734 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1735 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1741 bool swap_direction = false;
1743 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1744 swap_direction = true;
1747 framecnt_t frame_delta = 0;
1749 bool left_direction = false;
1750 if (last_pointer_frame() > adjusted_current_frame(event)) {
1751 left_direction = true;
1754 if (left_direction) {
1755 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1757 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1760 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1761 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1767 switch (_operation) {
1769 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1772 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1775 show_verbose_cursor_time (adjusted_current_frame (event));
1782 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1784 if (movement_occurred) {
1785 motion (event, false);
1787 /* This must happen before the region's StatefulDiffCommand is created, as it may
1788 `correct' (ahem) the region's _start from being negative to being zero. It
1789 needs to be zero in the undo record.
1791 if (_operation == StartTrim) {
1792 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1793 i->view->trim_front_ending ();
1797 if (!_editor->selection->selected (_primary)) {
1798 _primary->thaw_after_trim ();
1801 set<boost::shared_ptr<Playlist> > diffed_playlists;
1803 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1804 i->view->thaw_after_trim ();
1805 i->view->enable_display (true);
1806 i->view->fake_set_opaque (true);
1808 /* Trimming one region may affect others on the playlist, so we need
1809 to get undo Commands from the whole playlist rather than just the
1810 region. Use diffed_playlists to make sure we don't diff a given
1811 playlist more than once.
1813 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1814 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1815 vector<Command*> cmds;
1817 _editor->session()->add_commands (cmds);
1818 diffed_playlists.insert (p);
1822 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1826 _editor->motion_frozen_playlists.clear ();
1827 _editor->commit_reversible_command();
1830 /* no mouse movement */
1831 _editor->point_trim (event, adjusted_current_frame (event));
1834 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1835 if (_operation == StartTrim) {
1836 i->view->trim_front_ending ();
1839 i->view->region()->resume_property_changes ();
1844 TrimDrag::aborted (bool movement_occurred)
1846 /* Our motion method is changing model state, so use the Undo system
1847 to cancel. Perhaps not ideal, as this will leave an Undo point
1848 behind which may be slightly odd from the user's point of view.
1853 if (movement_occurred) {
1857 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1858 i->view->region()->resume_property_changes ();
1863 TrimDrag::setup_pointer_frame_offset ()
1865 list<DraggingView>::iterator i = _views.begin ();
1866 while (i != _views.end() && i->view != _primary) {
1870 if (i == _views.end()) {
1874 switch (_operation) {
1876 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1879 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1886 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1890 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1891 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1896 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1898 Drag::start_grab (event, cursor);
1899 show_verbose_cursor_time (adjusted_current_frame(event));
1903 MeterMarkerDrag::setup_pointer_frame_offset ()
1905 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1909 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1913 // create a dummy marker for visual representation of moving the
1914 // section, because whether its a copy or not, we're going to
1915 // leave or lose the original marker (leave if its a copy; lose if its
1916 // not, because we'll remove it from the map).
1918 MeterSection section (_marker->meter());
1920 if (!section.movable()) {
1925 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1927 _marker = new MeterMarker (
1929 *_editor->meter_group,
1930 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1932 *new MeterSection (_marker->meter())
1935 /* use the new marker for the grab */
1936 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1939 TempoMap& map (_editor->session()->tempo_map());
1940 /* get current state */
1941 before_state = &map.get_state();
1942 /* remove the section while we drag it */
1943 map.remove_meter (section, true);
1947 framepos_t const pf = adjusted_current_frame (event);
1948 _marker->set_position (pf);
1949 show_verbose_cursor_time (pf);
1953 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1955 if (!movement_occurred) {
1959 motion (event, false);
1961 Timecode::BBT_Time when;
1963 TempoMap& map (_editor->session()->tempo_map());
1964 map.bbt_time (last_pointer_frame(), when);
1966 if (_copy == true) {
1967 _editor->begin_reversible_command (_("copy meter mark"));
1968 XMLNode &before = map.get_state();
1969 map.add_meter (_marker->meter(), when);
1970 XMLNode &after = map.get_state();
1971 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1972 _editor->commit_reversible_command ();
1975 _editor->begin_reversible_command (_("move meter mark"));
1977 /* we removed it before, so add it back now */
1979 map.add_meter (_marker->meter(), when);
1980 XMLNode &after = map.get_state();
1981 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
1982 _editor->commit_reversible_command ();
1985 // delete the dummy marker we used for visual representation while moving.
1986 // a new visual marker will show up automatically.
1991 MeterMarkerDrag::aborted (bool moved)
1993 _marker->set_position (_marker->meter().frame ());
1996 TempoMap& map (_editor->session()->tempo_map());
1997 /* we removed it before, so add it back now */
1998 map.add_meter (_marker->meter(), _marker->meter().frame());
1999 // delete the dummy marker we used for visual representation while moving.
2000 // a new visual marker will show up automatically.
2005 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2009 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2011 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2016 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2018 Drag::start_grab (event, cursor);
2019 show_verbose_cursor_time (adjusted_current_frame (event));
2023 TempoMarkerDrag::setup_pointer_frame_offset ()
2025 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2029 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2033 // create a dummy marker for visual representation of moving the
2034 // section, because whether its a copy or not, we're going to
2035 // leave or lose the original marker (leave if its a copy; lose if its
2036 // not, because we'll remove it from the map).
2038 // create a dummy marker for visual representation of moving the copy.
2039 // The actual copying is not done before we reach the finish callback.
2042 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2044 TempoSection section (_marker->tempo());
2046 _marker = new TempoMarker (
2048 *_editor->tempo_group,
2049 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2051 *new TempoSection (_marker->tempo())
2054 /* use the new marker for the grab */
2055 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2058 TempoMap& map (_editor->session()->tempo_map());
2059 /* get current state */
2060 before_state = &map.get_state();
2061 /* remove the section while we drag it */
2062 map.remove_tempo (section, true);
2066 framepos_t const pf = adjusted_current_frame (event);
2067 _marker->set_position (pf);
2068 show_verbose_cursor_time (pf);
2072 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2074 if (!movement_occurred) {
2078 motion (event, false);
2080 TempoMap& map (_editor->session()->tempo_map());
2081 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2082 Timecode::BBT_Time when;
2084 map.bbt_time (beat_time, when);
2086 if (_copy == true) {
2087 _editor->begin_reversible_command (_("copy tempo mark"));
2088 XMLNode &before = map.get_state();
2089 map.add_tempo (_marker->tempo(), when);
2090 XMLNode &after = map.get_state();
2091 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2092 _editor->commit_reversible_command ();
2095 _editor->begin_reversible_command (_("move tempo mark"));
2096 /* we removed it before, so add it back now */
2097 map.add_tempo (_marker->tempo(), when);
2098 XMLNode &after = map.get_state();
2099 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2100 _editor->commit_reversible_command ();
2103 // delete the dummy marker we used for visual representation while moving.
2104 // a new visual marker will show up automatically.
2109 TempoMarkerDrag::aborted (bool moved)
2111 _marker->set_position (_marker->tempo().frame());
2113 TempoMap& map (_editor->session()->tempo_map());
2114 /* we removed it before, so add it back now */
2115 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2116 // delete the dummy marker we used for visual representation while moving.
2117 // a new visual marker will show up automatically.
2122 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2126 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2129 /** Do all the things we do when dragging the playhead to make it look as though
2130 * we have located, without actually doing the locate (because that would cause
2131 * the diskstream buffers to be refilled, which is too slow).
2134 CursorDrag::fake_locate (framepos_t t)
2136 _editor->playhead_cursor->set_position (t);
2138 Session* s = _editor->session ();
2139 if (s->timecode_transmission_suspended ()) {
2140 framepos_t const f = _editor->playhead_cursor->current_frame;
2141 s->send_mmc_locate (f);
2142 s->send_full_time_code (f);
2145 show_verbose_cursor_time (t);
2146 _editor->UpdateAllTransportClocks (t);
2150 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2152 Drag::start_grab (event, c);
2154 _grab_zoom = _editor->frames_per_unit;
2156 framepos_t where = _editor->event_frame (event, 0, 0);
2157 _editor->snap_to_with_modifier (where, event);
2159 _editor->_dragging_playhead = true;
2161 Session* s = _editor->session ();
2164 if (_was_rolling && _stop) {
2168 if (s->is_auditioning()) {
2169 s->cancel_audition ();
2172 s->request_suspend_timecode_transmission ();
2173 while (!s->timecode_transmission_suspended ()) {
2174 /* twiddle our thumbs */
2178 fake_locate (where);
2182 CursorDrag::motion (GdkEvent* event, bool)
2184 framepos_t const adjusted_frame = adjusted_current_frame (event);
2185 if (adjusted_frame != last_pointer_frame()) {
2186 fake_locate (adjusted_frame);
2188 _editor->update_canvas_now ();
2194 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2196 _editor->_dragging_playhead = false;
2198 if (!movement_occurred && _stop) {
2202 motion (event, false);
2204 Session* s = _editor->session ();
2206 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2207 _editor->_pending_locate_request = true;
2208 s->request_resume_timecode_transmission ();
2213 CursorDrag::aborted (bool)
2215 if (_editor->_dragging_playhead) {
2216 _editor->session()->request_resume_timecode_transmission ();
2217 _editor->_dragging_playhead = false;
2220 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2223 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2224 : RegionDrag (e, i, p, v)
2226 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2230 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2232 Drag::start_grab (event, cursor);
2234 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2235 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2237 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2239 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2243 FadeInDrag::setup_pointer_frame_offset ()
2245 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2246 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2247 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2251 FadeInDrag::motion (GdkEvent* event, bool)
2253 framecnt_t fade_length;
2255 framepos_t const pos = adjusted_current_frame (event);
2257 boost::shared_ptr<Region> region = _primary->region ();
2259 if (pos < (region->position() + 64)) {
2260 fade_length = 64; // this should be a minimum defined somewhere
2261 } else if (pos > region->last_frame()) {
2262 fade_length = region->length();
2264 fade_length = pos - region->position();
2267 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2269 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2275 tmp->reset_fade_in_shape_width (fade_length);
2276 tmp->show_fade_line((framecnt_t) fade_length);
2279 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2283 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2285 if (!movement_occurred) {
2289 framecnt_t fade_length;
2291 framepos_t const pos = adjusted_current_frame (event);
2293 boost::shared_ptr<Region> region = _primary->region ();
2295 if (pos < (region->position() + 64)) {
2296 fade_length = 64; // this should be a minimum defined somewhere
2297 } else if (pos > region->last_frame()) {
2298 fade_length = region->length();
2300 fade_length = pos - region->position();
2303 _editor->begin_reversible_command (_("change fade in length"));
2305 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2307 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2313 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2314 XMLNode &before = alist->get_state();
2316 tmp->audio_region()->set_fade_in_length (fade_length);
2317 tmp->audio_region()->set_fade_in_active (true);
2318 tmp->hide_fade_line();
2320 XMLNode &after = alist->get_state();
2321 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2324 _editor->commit_reversible_command ();
2328 FadeInDrag::aborted (bool)
2330 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2331 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2337 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2338 tmp->hide_fade_line();
2342 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2343 : RegionDrag (e, i, p, v)
2345 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2349 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2351 Drag::start_grab (event, cursor);
2353 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2354 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2356 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2358 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2362 FadeOutDrag::setup_pointer_frame_offset ()
2364 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2365 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2366 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2370 FadeOutDrag::motion (GdkEvent* event, bool)
2372 framecnt_t fade_length;
2374 framepos_t const pos = adjusted_current_frame (event);
2376 boost::shared_ptr<Region> region = _primary->region ();
2378 if (pos > (region->last_frame() - 64)) {
2379 fade_length = 64; // this should really be a minimum fade defined somewhere
2381 else if (pos < region->position()) {
2382 fade_length = region->length();
2385 fade_length = region->last_frame() - pos;
2388 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2390 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2396 tmp->reset_fade_out_shape_width (fade_length);
2397 tmp->show_fade_line(region->length() - fade_length);
2400 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2404 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2406 if (!movement_occurred) {
2410 framecnt_t fade_length;
2412 framepos_t const pos = adjusted_current_frame (event);
2414 boost::shared_ptr<Region> region = _primary->region ();
2416 if (pos > (region->last_frame() - 64)) {
2417 fade_length = 64; // this should really be a minimum fade defined somewhere
2419 else if (pos < region->position()) {
2420 fade_length = region->length();
2423 fade_length = region->last_frame() - pos;
2426 _editor->begin_reversible_command (_("change fade out length"));
2428 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2430 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2436 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2437 XMLNode &before = alist->get_state();
2439 tmp->audio_region()->set_fade_out_length (fade_length);
2440 tmp->audio_region()->set_fade_out_active (true);
2441 tmp->hide_fade_line();
2443 XMLNode &after = alist->get_state();
2444 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2447 _editor->commit_reversible_command ();
2451 FadeOutDrag::aborted (bool)
2453 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2454 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2460 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2461 tmp->hide_fade_line();
2465 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2468 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2470 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2473 _points.push_back (Gnome::Art::Point (0, 0));
2474 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2477 MarkerDrag::~MarkerDrag ()
2479 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2485 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2487 Drag::start_grab (event, cursor);
2491 Location *location = _editor->find_location_from_marker (_marker, is_start);
2492 _editor->_dragging_edit_point = true;
2494 update_item (location);
2496 // _drag_line->show();
2497 // _line->raise_to_top();
2500 show_verbose_cursor_time (location->start());
2502 show_verbose_cursor_time (location->end());
2505 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2508 case Selection::Toggle:
2509 _editor->selection->toggle (_marker);
2511 case Selection::Set:
2512 if (!_editor->selection->selected (_marker)) {
2513 _editor->selection->set (_marker);
2516 case Selection::Extend:
2518 Locations::LocationList ll;
2519 list<Marker*> to_add;
2521 _editor->selection->markers.range (s, e);
2522 s = min (_marker->position(), s);
2523 e = max (_marker->position(), e);
2526 if (e < max_framepos) {
2529 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2530 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2531 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2534 to_add.push_back (lm->start);
2537 to_add.push_back (lm->end);
2541 if (!to_add.empty()) {
2542 _editor->selection->add (to_add);
2546 case Selection::Add:
2547 _editor->selection->add (_marker);
2551 /* Set up copies for us to manipulate during the drag */
2553 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2554 Location* l = _editor->find_location_from_marker (*i, is_start);
2555 _copied_locations.push_back (new Location (*l));
2560 MarkerDrag::setup_pointer_frame_offset ()
2563 Location *location = _editor->find_location_from_marker (_marker, is_start);
2564 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2568 MarkerDrag::motion (GdkEvent* event, bool)
2570 framecnt_t f_delta = 0;
2572 bool move_both = false;
2574 Location *real_location;
2575 Location *copy_location = 0;
2577 framepos_t const newframe = adjusted_current_frame (event);
2579 framepos_t next = newframe;
2581 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2585 MarkerSelection::iterator i;
2586 list<Location*>::iterator x;
2588 /* find the marker we're dragging, and compute the delta */
2590 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2591 x != _copied_locations.end() && i != _editor->selection->markers.end();
2597 if (marker == _marker) {
2599 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2604 if (real_location->is_mark()) {
2605 f_delta = newframe - copy_location->start();
2609 switch (marker->type()) {
2610 case Marker::SessionStart:
2611 case Marker::RangeStart:
2612 case Marker::LoopStart:
2613 case Marker::PunchIn:
2614 f_delta = newframe - copy_location->start();
2617 case Marker::SessionEnd:
2618 case Marker::RangeEnd:
2619 case Marker::LoopEnd:
2620 case Marker::PunchOut:
2621 f_delta = newframe - copy_location->end();
2624 /* what kind of marker is this ? */
2632 if (i == _editor->selection->markers.end()) {
2633 /* hmm, impossible - we didn't find the dragged marker */
2637 /* now move them all */
2639 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2640 x != _copied_locations.end() && i != _editor->selection->markers.end();
2646 /* call this to find out if its the start or end */
2648 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2652 if (real_location->locked()) {
2656 if (copy_location->is_mark()) {
2660 copy_location->set_start (copy_location->start() + f_delta);
2664 framepos_t new_start = copy_location->start() + f_delta;
2665 framepos_t new_end = copy_location->end() + f_delta;
2667 if (is_start) { // start-of-range marker
2670 copy_location->set_start (new_start);
2671 copy_location->set_end (new_end);
2672 } else if (new_start < copy_location->end()) {
2673 copy_location->set_start (new_start);
2674 } else if (newframe > 0) {
2675 _editor->snap_to (next, 1, true);
2676 copy_location->set_end (next);
2677 copy_location->set_start (newframe);
2680 } else { // end marker
2683 copy_location->set_end (new_end);
2684 copy_location->set_start (new_start);
2685 } else if (new_end > copy_location->start()) {
2686 copy_location->set_end (new_end);
2687 } else if (newframe > 0) {
2688 _editor->snap_to (next, -1, true);
2689 copy_location->set_start (next);
2690 copy_location->set_end (newframe);
2695 update_item (copy_location);
2697 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2700 lm->set_position (copy_location->start(), copy_location->end());
2704 assert (!_copied_locations.empty());
2706 show_verbose_cursor_time (newframe);
2709 _editor->update_canvas_now ();
2714 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2716 if (!movement_occurred) {
2718 /* just a click, do nothing but finish
2719 off the selection process
2722 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2725 case Selection::Set:
2726 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2727 _editor->selection->set (_marker);
2731 case Selection::Toggle:
2732 case Selection::Extend:
2733 case Selection::Add:
2740 _editor->_dragging_edit_point = false;
2742 _editor->begin_reversible_command ( _("move marker") );
2743 XMLNode &before = _editor->session()->locations()->get_state();
2745 MarkerSelection::iterator i;
2746 list<Location*>::iterator x;
2749 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2750 x != _copied_locations.end() && i != _editor->selection->markers.end();
2753 Location * location = _editor->find_location_from_marker (*i, is_start);
2757 if (location->locked()) {
2761 if (location->is_mark()) {
2762 location->set_start ((*x)->start());
2764 location->set ((*x)->start(), (*x)->end());
2769 XMLNode &after = _editor->session()->locations()->get_state();
2770 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2771 _editor->commit_reversible_command ();
2775 MarkerDrag::aborted (bool)
2781 MarkerDrag::update_item (Location*)
2786 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2788 _cumulative_x_drag (0),
2789 _cumulative_y_drag (0)
2791 if (_zero_gain_fraction < 0.0) {
2792 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2795 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2797 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2803 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2805 Drag::start_grab (event, _editor->cursors()->fader);
2807 // start the grab at the center of the control point so
2808 // the point doesn't 'jump' to the mouse after the first drag
2809 _fixed_grab_x = _point->get_x();
2810 _fixed_grab_y = _point->get_y();
2812 float const fraction = 1 - (_point->get_y() / _point->line().height());
2814 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2816 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2817 event->button.x + 10, event->button.y + 10);
2819 _editor->verbose_cursor()->show ();
2823 ControlPointDrag::motion (GdkEvent* event, bool)
2825 double dx = _drags->current_pointer_x() - last_pointer_x();
2826 double dy = _drags->current_pointer_y() - last_pointer_y();
2828 if (event->button.state & Keyboard::SecondaryModifier) {
2833 /* coordinate in pixels relative to the start of the region (for region-based automation)
2834 or track (for track-based automation) */
2835 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2836 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2838 // calculate zero crossing point. back off by .01 to stay on the
2839 // positive side of zero
2840 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2842 // make sure we hit zero when passing through
2843 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2847 if (_x_constrained) {
2850 if (_y_constrained) {
2854 _cumulative_x_drag = cx - _fixed_grab_x;
2855 _cumulative_y_drag = cy - _fixed_grab_y;
2859 cy = min ((double) _point->line().height(), cy);
2861 framepos_t cx_frames = _editor->unit_to_frame (cx);
2863 if (!_x_constrained) {
2864 _editor->snap_to_with_modifier (cx_frames, event);
2867 cx_frames = min (cx_frames, _point->line().maximum_time());
2869 float const fraction = 1.0 - (cy / _point->line().height());
2871 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2873 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2875 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2879 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2881 if (!movement_occurred) {
2885 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2886 _editor->reset_point_selection ();
2890 motion (event, false);
2893 _point->line().end_drag ();
2894 _editor->session()->commit_reversible_command ();
2898 ControlPointDrag::aborted (bool)
2900 _point->line().reset ();
2904 ControlPointDrag::active (Editing::MouseMode m)
2906 if (m == Editing::MouseGain) {
2907 /* always active in mouse gain */
2911 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2912 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2915 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2918 _cumulative_y_drag (0)
2920 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2924 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2926 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2929 _item = &_line->grab_item ();
2931 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2932 origin, and ditto for y.
2935 double cx = event->button.x;
2936 double cy = event->button.y;
2938 _line->parent_group().w2i (cx, cy);
2940 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2945 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2946 /* no adjacent points */
2950 Drag::start_grab (event, _editor->cursors()->fader);
2952 /* store grab start in parent frame */
2957 double fraction = 1.0 - (cy / _line->height());
2959 _line->start_drag_line (before, after, fraction);
2961 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2962 event->button.x + 10, event->button.y + 10);
2964 _editor->verbose_cursor()->show ();
2968 LineDrag::motion (GdkEvent* event, bool)
2970 double dy = _drags->current_pointer_y() - last_pointer_y();
2972 if (event->button.state & Keyboard::SecondaryModifier) {
2976 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2978 _cumulative_y_drag = cy - _fixed_grab_y;
2981 cy = min ((double) _line->height(), cy);
2983 double const fraction = 1.0 - (cy / _line->height());
2987 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2993 /* we are ignoring x position for this drag, so we can just pass in anything */
2994 _line->drag_motion (0, fraction, true, push);
2996 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3000 LineDrag::finished (GdkEvent* event, bool)
3002 motion (event, false);
3004 _editor->session()->commit_reversible_command ();
3008 LineDrag::aborted (bool)
3013 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3016 _cumulative_x_drag (0)
3018 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3022 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3024 Drag::start_grab (event);
3026 _line = reinterpret_cast<Line*> (_item);
3029 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3031 double cx = event->button.x;
3032 double cy = event->button.y;
3034 _item->property_parent().get_value()->w2i(cx, cy);
3036 /* store grab start in parent frame */
3037 _region_view_grab_x = cx;
3039 _before = *(float*) _item->get_data ("position");
3041 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3043 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3047 FeatureLineDrag::motion (GdkEvent*, bool)
3049 double dx = _drags->current_pointer_x() - last_pointer_x();
3051 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3053 _cumulative_x_drag += dx;
3055 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3064 ArdourCanvas::Points points;
3066 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3068 _line->get_bounds(x1, y2, x2, y2);
3070 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3071 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3073 _line->property_points() = points;
3075 float *pos = new float;
3078 _line->set_data ("position", pos);
3084 FeatureLineDrag::finished (GdkEvent*, bool)
3086 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3087 _arv->update_transient(_before, _before);
3091 FeatureLineDrag::aborted (bool)
3096 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3098 , _vertical_only (false)
3100 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3104 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3106 Drag::start_grab (event);
3107 show_verbose_cursor_time (adjusted_current_frame (event));
3111 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3118 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3120 framepos_t grab = grab_frame ();
3121 if (Config->get_rubberbanding_snaps_to_grid ()) {
3122 _editor->snap_to_with_modifier (grab, event);
3125 /* base start and end on initial click position */
3135 if (_drags->current_pointer_y() < grab_y()) {
3136 y1 = _drags->current_pointer_y();
3139 y2 = _drags->current_pointer_y();
3144 if (start != end || y1 != y2) {
3146 double x1 = _editor->frame_to_pixel (start);
3147 double x2 = _editor->frame_to_pixel (end);
3149 _editor->rubberband_rect->property_x1() = x1;
3150 if (_vertical_only) {
3151 /* fixed 10 pixel width */
3152 _editor->rubberband_rect->property_x2() = x1 + 10;
3154 _editor->rubberband_rect->property_x2() = x2;
3157 _editor->rubberband_rect->property_y1() = y1;
3158 _editor->rubberband_rect->property_y2() = y2;
3160 _editor->rubberband_rect->show();
3161 _editor->rubberband_rect->raise_to_top();
3163 show_verbose_cursor_time (pf);
3165 do_select_things (event, true);
3170 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3175 if (grab_frame() < last_pointer_frame()) {
3177 x2 = last_pointer_frame ();
3180 x1 = last_pointer_frame ();
3186 if (_drags->current_pointer_y() < grab_y()) {
3187 y1 = _drags->current_pointer_y();
3190 y2 = _drags->current_pointer_y();
3194 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3198 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3200 if (movement_occurred) {
3202 motion (event, false);
3203 do_select_things (event, false);
3209 bool do_deselect = true;
3210 MidiTimeAxisView* mtv;
3212 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3214 if (_editor->selection->empty()) {
3215 /* nothing selected */
3216 add_midi_region (mtv);
3217 do_deselect = false;
3227 _editor->rubberband_rect->hide();
3231 RubberbandSelectDrag::aborted (bool)
3233 _editor->rubberband_rect->hide ();
3236 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3237 : RegionDrag (e, i, p, v)
3239 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3243 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3245 Drag::start_grab (event, cursor);
3247 show_verbose_cursor_time (adjusted_current_frame (event));
3251 TimeFXDrag::motion (GdkEvent* event, bool)
3253 RegionView* rv = _primary;
3255 framepos_t const pf = adjusted_current_frame (event);
3257 if (pf > rv->region()->position()) {
3258 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3261 show_verbose_cursor_time (pf);
3265 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3267 _primary->get_time_axis_view().hide_timestretch ();
3269 if (!movement_occurred) {
3273 if (last_pointer_frame() < _primary->region()->position()) {
3274 /* backwards drag of the left edge - not usable */
3278 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3280 float percentage = (double) newlen / (double) _primary->region()->length();
3282 #ifndef USE_RUBBERBAND
3283 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3284 if (_primary->region()->data_type() == DataType::AUDIO) {
3285 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3289 if (!_editor->get_selection().regions.empty()) {
3290 /* primary will already be included in the selection, and edit
3291 group shared editing will propagate selection across
3292 equivalent regions, so just use the current region
3296 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3297 error << _("An error occurred while executing time stretch operation") << endmsg;
3303 TimeFXDrag::aborted (bool)
3305 _primary->get_time_axis_view().hide_timestretch ();
3308 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3311 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3315 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3317 Drag::start_grab (event);
3321 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3323 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3327 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3329 if (movement_occurred && _editor->session()) {
3330 /* make sure we stop */
3331 _editor->session()->request_transport_speed (0.0);
3336 ScrubDrag::aborted (bool)
3341 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3345 , _original_pointer_time_axis (-1)
3346 , _last_pointer_time_axis (-1)
3347 , _time_selection_at_start (!_editor->get_selection().time.empty())
3349 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3353 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3355 if (_editor->session() == 0) {
3359 Gdk::Cursor* cursor = 0;
3361 switch (_operation) {
3362 case CreateSelection:
3363 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3368 cursor = _editor->cursors()->selector;
3369 Drag::start_grab (event, cursor);
3372 case SelectionStartTrim:
3373 if (_editor->clicked_axisview) {
3374 _editor->clicked_axisview->order_selection_trims (_item, true);
3376 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3379 case SelectionEndTrim:
3380 if (_editor->clicked_axisview) {
3381 _editor->clicked_axisview->order_selection_trims (_item, false);
3383 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3387 Drag::start_grab (event, cursor);
3391 if (_operation == SelectionMove) {
3392 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3394 show_verbose_cursor_time (adjusted_current_frame (event));
3397 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3401 SelectionDrag::setup_pointer_frame_offset ()
3403 switch (_operation) {
3404 case CreateSelection:
3405 _pointer_frame_offset = 0;
3408 case SelectionStartTrim:
3410 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3413 case SelectionEndTrim:
3414 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3420 SelectionDrag::motion (GdkEvent* event, bool first_move)
3422 framepos_t start = 0;
3426 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3427 if (pending_time_axis.first == 0) {
3431 framepos_t const pending_position = adjusted_current_frame (event);
3433 /* only alter selection if things have changed */
3435 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3439 switch (_operation) {
3440 case CreateSelection:
3442 framepos_t grab = grab_frame ();
3445 _editor->snap_to (grab);
3448 if (pending_position < grab_frame()) {
3449 start = pending_position;
3452 end = pending_position;
3456 /* first drag: Either add to the selection
3457 or create a new selection
3463 /* adding to the selection */
3464 _editor->set_selected_track_as_side_effect (Selection::Add);
3465 //_editor->selection->add (_editor->clicked_axisview);
3466 _editor->clicked_selection = _editor->selection->add (start, end);
3471 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3472 //_editor->selection->set (_editor->clicked_axisview);
3473 _editor->set_selected_track_as_side_effect (Selection::Set);
3476 _editor->clicked_selection = _editor->selection->set (start, end);
3480 /* select the track that we're in */
3481 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3482 // _editor->set_selected_track_as_side_effect (Selection::Add);
3483 _editor->selection->add (pending_time_axis.first);
3484 _added_time_axes.push_back (pending_time_axis.first);
3487 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3488 tracks that we selected in the first place.
3491 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3492 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3494 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3495 while (i != _added_time_axes.end()) {
3497 list<TimeAxisView*>::iterator tmp = i;
3500 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3501 _editor->selection->remove (*i);
3502 _added_time_axes.remove (*i);
3511 case SelectionStartTrim:
3513 start = _editor->selection->time[_editor->clicked_selection].start;
3514 end = _editor->selection->time[_editor->clicked_selection].end;
3516 if (pending_position > end) {
3519 start = pending_position;
3523 case SelectionEndTrim:
3525 start = _editor->selection->time[_editor->clicked_selection].start;
3526 end = _editor->selection->time[_editor->clicked_selection].end;
3528 if (pending_position < start) {
3531 end = pending_position;
3538 start = _editor->selection->time[_editor->clicked_selection].start;
3539 end = _editor->selection->time[_editor->clicked_selection].end;
3541 length = end - start;
3543 start = pending_position;
3544 _editor->snap_to (start);
3546 end = start + length;
3551 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3552 _editor->start_canvas_autoscroll (1, 0);
3556 _editor->selection->replace (_editor->clicked_selection, start, end);
3559 if (_operation == SelectionMove) {
3560 show_verbose_cursor_time(start);
3562 show_verbose_cursor_time(pending_position);
3567 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3569 Session* s = _editor->session();
3571 if (movement_occurred) {
3572 motion (event, false);
3573 /* XXX this is not object-oriented programming at all. ick */
3574 if (_editor->selection->time.consolidate()) {
3575 _editor->selection->TimeChanged ();
3578 /* XXX what if its a music time selection? */
3580 if ((s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3581 s->request_play_range (&_editor->selection->time, true);
3583 if (Config->get_always_play_range()) {
3584 if (_editor->doing_range_stuff()) {
3585 s->request_locate (_editor->get_selection().time.start());
3592 /* just a click, no pointer movement.
3595 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3596 if (!_time_selection_at_start) {
3597 if (_editor->clicked_regionview) {
3598 if (_editor->get_selection().selected (_editor->clicked_regionview)) {
3599 /* range select the entire current
3602 _editor->select_range (_editor->get_selection().regions.start(),
3603 _editor->get_selection().regions.end_frame());
3605 /* range select this (unselected)
3608 _editor->select_range (_editor->clicked_regionview->region()->position(),
3609 _editor->clicked_regionview->region()->last_frame());
3613 _editor->selection->clear_time();
3617 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3618 _editor->selection->set (_editor->clicked_axisview);
3621 if (s && s->get_play_range () && s->transport_rolling()) {
3622 s->request_stop (false, false);
3625 if (Config->get_always_play_range()) {
3626 if (_editor->doing_range_stuff()) {
3627 s->request_locate (_editor->get_selection().time.start());
3632 _editor->stop_canvas_autoscroll ();
3636 SelectionDrag::aborted (bool)
3641 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3646 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3648 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3649 physical_screen_height (_editor->get_window()));
3650 _drag_rect->hide ();
3652 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3653 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3657 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3659 if (_editor->session() == 0) {
3663 Gdk::Cursor* cursor = 0;
3665 if (!_editor->temp_location) {
3666 _editor->temp_location = new Location (*_editor->session());
3669 switch (_operation) {
3670 case CreateRangeMarker:
3671 case CreateTransportMarker:
3672 case CreateCDMarker:
3674 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3679 cursor = _editor->cursors()->selector;
3683 Drag::start_grab (event, cursor);
3685 show_verbose_cursor_time (adjusted_current_frame (event));
3689 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3691 framepos_t start = 0;
3693 ArdourCanvas::SimpleRect *crect;
3695 switch (_operation) {
3696 case CreateRangeMarker:
3697 crect = _editor->range_bar_drag_rect;
3699 case CreateTransportMarker:
3700 crect = _editor->transport_bar_drag_rect;
3702 case CreateCDMarker:
3703 crect = _editor->cd_marker_bar_drag_rect;
3706 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3711 framepos_t const pf = adjusted_current_frame (event);
3713 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3714 framepos_t grab = grab_frame ();
3715 _editor->snap_to (grab);
3717 if (pf < grab_frame()) {
3725 /* first drag: Either add to the selection
3726 or create a new selection.
3731 _editor->temp_location->set (start, end);
3735 update_item (_editor->temp_location);
3737 //_drag_rect->raise_to_top();
3742 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3743 _editor->start_canvas_autoscroll (1, 0);
3747 _editor->temp_location->set (start, end);
3749 double x1 = _editor->frame_to_pixel (start);
3750 double x2 = _editor->frame_to_pixel (end);
3751 crect->property_x1() = x1;
3752 crect->property_x2() = x2;
3754 update_item (_editor->temp_location);
3757 show_verbose_cursor_time (pf);
3762 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3764 Location * newloc = 0;
3768 if (movement_occurred) {
3769 motion (event, false);
3772 switch (_operation) {
3773 case CreateRangeMarker:
3774 case CreateCDMarker:
3776 _editor->begin_reversible_command (_("new range marker"));
3777 XMLNode &before = _editor->session()->locations()->get_state();
3778 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3779 if (_operation == CreateCDMarker) {
3780 flags = Location::IsRangeMarker | Location::IsCDMarker;
3781 _editor->cd_marker_bar_drag_rect->hide();
3784 flags = Location::IsRangeMarker;
3785 _editor->range_bar_drag_rect->hide();
3787 newloc = new Location (
3788 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3791 _editor->session()->locations()->add (newloc, true);
3792 XMLNode &after = _editor->session()->locations()->get_state();
3793 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3794 _editor->commit_reversible_command ();
3798 case CreateTransportMarker:
3799 // popup menu to pick loop or punch
3800 _editor->new_transport_marker_context_menu (&event->button, _item);
3804 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3806 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3811 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3813 if (end == max_framepos) {
3814 end = _editor->session()->current_end_frame ();
3817 if (start == max_framepos) {
3818 start = _editor->session()->current_start_frame ();
3821 switch (_editor->mouse_mode) {
3823 /* find the two markers on either side and then make the selection from it */
3824 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3828 /* find the two markers on either side of the click and make the range out of it */
3829 _editor->selection->set (start, end);
3838 _editor->stop_canvas_autoscroll ();
3842 RangeMarkerBarDrag::aborted (bool)
3848 RangeMarkerBarDrag::update_item (Location* location)
3850 double const x1 = _editor->frame_to_pixel (location->start());
3851 double const x2 = _editor->frame_to_pixel (location->end());
3853 _drag_rect->property_x1() = x1;
3854 _drag_rect->property_x2() = x2;
3857 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3861 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3865 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3867 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3868 Drag::start_grab (event, _editor->cursors()->zoom_out);
3871 Drag::start_grab (event, _editor->cursors()->zoom_in);
3875 show_verbose_cursor_time (adjusted_current_frame (event));
3879 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3884 framepos_t const pf = adjusted_current_frame (event);
3886 framepos_t grab = grab_frame ();
3887 _editor->snap_to_with_modifier (grab, event);
3889 /* base start and end on initial click position */
3901 _editor->zoom_rect->show();
3902 _editor->zoom_rect->raise_to_top();
3905 _editor->reposition_zoom_rect(start, end);
3907 show_verbose_cursor_time (pf);
3912 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3914 if (movement_occurred) {
3915 motion (event, false);
3917 if (grab_frame() < last_pointer_frame()) {
3918 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
3920 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
3923 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3924 _editor->tav_zoom_step (_zoom_out);
3926 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3930 _editor->zoom_rect->hide();
3934 MouseZoomDrag::aborted (bool)
3936 _editor->zoom_rect->hide ();
3939 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3941 , _cumulative_dx (0)
3942 , _cumulative_dy (0)
3944 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3946 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3947 _region = &_primary->region_view ();
3948 _note_height = _region->midi_stream_view()->note_height ();
3952 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3954 Drag::start_grab (event);
3956 if (!(_was_selected = _primary->selected())) {
3958 /* tertiary-click means extend selection - we'll do that on button release,
3959 so don't add it here, because otherwise we make it hard to figure
3960 out the "extend-to" range.
3963 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3966 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3969 _region->note_selected (_primary, true);
3971 _region->unique_select (_primary);
3977 /** @return Current total drag x change in frames */
3979 NoteDrag::total_dx () const
3982 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3984 /* primary note time */
3985 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3987 /* new time of the primary note in session frames */
3988 frameoffset_t st = n + dx;
3990 framepos_t const rp = _region->region()->position ();
3992 /* prevent the note being dragged earlier than the region's position */
3995 /* snap and return corresponding delta */
3996 return _region->snap_frame_to_frame (st - rp) + rp - n;
3999 /** @return Current total drag y change in note number */
4001 NoteDrag::total_dy () const
4003 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
4007 NoteDrag::motion (GdkEvent *, bool)
4009 /* Total change in x and y since the start of the drag */
4010 frameoffset_t const dx = total_dx ();
4011 int8_t const dy = total_dy ();
4013 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4014 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4015 double const tdy = -dy * _note_height - _cumulative_dy;
4018 _cumulative_dx += tdx;
4019 _cumulative_dy += tdy;
4021 int8_t note_delta = total_dy();
4023 _region->move_selection (tdx, tdy, note_delta);
4025 /* the new note value may be the same as the old one, but we
4026 * don't know what that means because the selection may have
4027 * involved more than one note and we might be doing something
4028 * odd with them. so show the note value anyway, always.
4032 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4034 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4035 (int) floor (new_note));
4037 show_verbose_cursor_text (buf);
4042 NoteDrag::finished (GdkEvent* ev, bool moved)
4045 /* no motion - select note */
4047 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4048 _editor->current_mouse_mode() == Editing::MouseDraw) {
4050 if (_was_selected) {
4051 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4053 _region->note_deselected (_primary);
4056 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4057 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4059 if (!extend && !add && _region->selection_size() > 1) {
4060 _region->unique_select (_primary);
4061 } else if (extend) {
4062 _region->note_selected (_primary, true, true);
4064 /* it was added during button press */
4069 _region->note_dropped (_primary, total_dx(), total_dy());
4074 NoteDrag::aborted (bool)
4079 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4080 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4081 : Drag (editor, atv->base_item ())
4083 , _nothing_to_drag (false)
4085 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4087 setup (atv->lines ());
4090 /** Make an AutomationRangeDrag for region gain lines */
4091 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4092 : Drag (editor, rv->get_canvas_group ())
4094 , _nothing_to_drag (false)
4096 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4098 list<boost::shared_ptr<AutomationLine> > lines;
4099 lines.push_back (rv->get_gain_line ());
4103 /** @param lines AutomationLines to drag.
4104 * @param offset Offset from the session start to the points in the AutomationLines.
4107 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4109 /* find the lines that overlap the ranges being dragged */
4110 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4111 while (i != lines.end ()) {
4112 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4115 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4117 /* check this range against all the AudioRanges that we are using */
4118 list<AudioRange>::const_iterator k = _ranges.begin ();
4119 while (k != _ranges.end()) {
4120 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4126 /* add it to our list if it overlaps at all */
4127 if (k != _ranges.end()) {
4132 _lines.push_back (n);
4138 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4142 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4144 Drag::start_grab (event, cursor);
4146 /* Get line states before we start changing things */
4147 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4148 i->state = &i->line->get_state ();
4151 if (_ranges.empty()) {
4153 /* No selected time ranges: drag all points */
4154 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4155 uint32_t const N = i->line->npoints ();
4156 for (uint32_t j = 0; j < N; ++j) {
4157 i->points.push_back (i->line->nth (j));
4163 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4165 framecnt_t const half = (i->start + i->end) / 2;
4167 /* find the line that this audio range starts in */
4168 list<Line>::iterator j = _lines.begin();
4169 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4173 if (j != _lines.end()) {
4174 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4176 /* j is the line that this audio range starts in; fade into it;
4177 64 samples length plucked out of thin air.
4180 framepos_t a = i->start + 64;
4185 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4186 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4188 the_list->add (p, the_list->eval (p));
4189 j->line->add_always_in_view (p);
4190 the_list->add (q, the_list->eval (q));
4191 j->line->add_always_in_view (q);
4194 /* same thing for the end */
4197 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4201 if (j != _lines.end()) {
4202 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4204 /* j is the line that this audio range starts in; fade out of it;
4205 64 samples length plucked out of thin air.
4208 framepos_t b = i->end - 64;
4213 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4214 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4216 the_list->add (p, the_list->eval (p));
4217 j->line->add_always_in_view (p);
4218 the_list->add (q, the_list->eval (q));
4219 j->line->add_always_in_view (q);
4223 _nothing_to_drag = true;
4225 /* Find all the points that should be dragged and put them in the relevant
4226 points lists in the Line structs.
4229 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4231 uint32_t const N = i->line->npoints ();
4232 for (uint32_t j = 0; j < N; ++j) {
4234 /* here's a control point on this line */
4235 ControlPoint* p = i->line->nth (j);
4236 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4238 /* see if it's inside a range */
4239 list<AudioRange>::const_iterator k = _ranges.begin ();
4240 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4244 if (k != _ranges.end()) {
4245 /* dragging this point */
4246 _nothing_to_drag = false;
4247 i->points.push_back (p);
4253 if (_nothing_to_drag) {
4257 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4258 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4263 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4265 if (_nothing_to_drag) {
4269 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4270 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4272 /* we are ignoring x position for this drag, so we can just pass in anything */
4273 i->line->drag_motion (0, f, true, false);
4278 AutomationRangeDrag::finished (GdkEvent* event, bool)
4280 if (_nothing_to_drag) {
4284 motion (event, false);
4285 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4286 i->line->end_drag ();
4287 i->line->clear_always_in_view ();
4290 _editor->session()->commit_reversible_command ();
4294 AutomationRangeDrag::aborted (bool)
4296 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4297 i->line->clear_always_in_view ();
4302 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4305 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4306 layer = v->region()->layer ();
4307 initial_y = v->get_canvas_group()->property_y ();
4308 initial_playlist = v->region()->playlist ();
4309 initial_position = v->region()->position ();
4310 initial_end = v->region()->position () + v->region()->length ();
4313 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4317 , _cumulative_dx (0)
4319 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4323 PatchChangeDrag::motion (GdkEvent* ev, bool)
4325 framepos_t f = adjusted_current_frame (ev);
4326 boost::shared_ptr<Region> r = _region_view->region ();
4327 f = max (f, r->position ());
4328 f = min (f, r->last_frame ());
4330 framecnt_t const dxf = f - grab_frame();
4331 double const dxu = _editor->frame_to_unit (dxf);
4332 _patch_change->move (dxu - _cumulative_dx, 0);
4333 _cumulative_dx = dxu;
4337 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4339 if (!movement_occurred) {
4343 boost::shared_ptr<Region> r (_region_view->region ());
4345 framepos_t f = adjusted_current_frame (ev);
4346 f = max (f, r->position ());
4347 f = min (f, r->last_frame ());
4349 _region_view->move_patch_change (
4351 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4356 PatchChangeDrag::aborted (bool)
4358 _patch_change->move (-_cumulative_dx, 0);
4362 PatchChangeDrag::setup_pointer_frame_offset ()
4364 boost::shared_ptr<Region> region = _region_view->region ();
4365 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4368 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4369 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4376 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4378 framepos_t const p = _region_view->region()->position ();
4379 double const y = _region_view->midi_view()->y_position ();
4381 x1 = max ((framepos_t) 0, x1 - p);
4382 x2 = max ((framepos_t) 0, x2 - p);
4383 y1 = max (0.0, y1 - y);
4384 y2 = max (0.0, y2 - y);
4386 _region_view->update_drag_selection (
4387 _editor->frame_to_pixel (x1),
4388 _editor->frame_to_pixel (x2),
4391 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4396 MidiRubberbandSelectDrag::deselect_things ()
4401 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4402 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4405 _vertical_only = true;
4409 MidiVerticalSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4411 double const y = _region_view->midi_view()->y_position ();
4413 y1 = max (0.0, y1 - y);
4414 y2 = max (0.0, y2 - y);
4416 _region_view->update_vertical_drag_selection (
4419 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4424 MidiVerticalSelectDrag::deselect_things ()
4429 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4430 : RubberbandSelectDrag (e, i)
4436 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4438 if (drag_in_progress) {
4439 /* We just want to select things at the end of the drag, not during it */
4443 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4445 _editor->begin_reversible_command (_("rubberband selection"));
4446 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4447 _editor->commit_reversible_command ();
4451 EditorRubberbandSelectDrag::deselect_things ()
4453 if (!getenv("ARDOUR_SAE")) {
4454 _editor->selection->clear_tracks();
4456 _editor->selection->clear_regions();
4457 _editor->selection->clear_points ();
4458 _editor->selection->clear_lines ();
4461 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4469 NoteCreateDrag::~NoteCreateDrag ()
4475 NoteCreateDrag::grid_frames (framepos_t t) const
4478 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4483 return _region_view->region_beats_to_region_frames (grid_beats);
4487 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4489 Drag::start_grab (event, cursor);
4491 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4493 framepos_t pf = _drags->current_pointer_frame ();
4494 framecnt_t const g = grid_frames (pf);
4496 /* Hack so that we always snap to the note that we are over, instead of snapping
4497 to the next one if we're more than halfway through the one we're over.
4499 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4503 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4505 MidiStreamView* sv = _region_view->midi_stream_view ();
4506 double const x = _editor->frame_to_pixel (_note[0]);
4507 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4509 _drag_rect->property_x1() = x;
4510 _drag_rect->property_y1() = y;
4511 _drag_rect->property_x2() = x;
4512 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4514 _drag_rect->property_outline_what() = 0xff;
4515 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4516 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4520 NoteCreateDrag::motion (GdkEvent* event, bool)
4522 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4523 double const x = _editor->frame_to_pixel (_note[1]);
4524 if (_note[1] > _note[0]) {
4525 _drag_rect->property_x2() = x;
4527 _drag_rect->property_x1() = x;
4532 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4534 if (!had_movement) {
4538 framepos_t const start = min (_note[0], _note[1]);
4539 framecnt_t length = abs (_note[0] - _note[1]);
4541 framecnt_t const g = grid_frames (start);
4542 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4544 if (_editor->snap_mode() == SnapNormal && length < g) {
4545 length = g - one_tick;
4548 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4550 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4554 NoteCreateDrag::y_to_region (double y) const
4557 _region_view->get_canvas_group()->w2i (x, y);
4562 NoteCreateDrag::aborted (bool)