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.
20 #define __STDC_LIMIT_MACROS 1
22 #include "pbd/memento_command.h"
23 #include "pbd/basename.h"
24 #include "pbd/stateful_diff_command.h"
25 #include "ardour/session.h"
26 #include "ardour/dB.h"
27 #include "ardour/region_factory.h"
31 #include "audio_region_view.h"
32 #include "midi_region_view.h"
33 #include "ardour_ui.h"
34 #include "gui_thread.h"
35 #include "control_point.h"
37 #include "region_gain_line.h"
38 #include "editor_drag.h"
39 #include "audio_time_axis.h"
40 #include "midi_time_axis.h"
41 #include "canvas-note.h"
42 #include "selection.h"
43 #include "midi_selection.h"
44 #include "automation_time_axis.h"
47 using namespace ARDOUR;
50 using namespace Editing;
51 using namespace ArdourCanvas;
53 using Gtkmm2ext::Keyboard;
55 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
57 DragManager::DragManager (Editor* e)
60 , _current_pointer_frame (0)
65 DragManager::~DragManager ()
70 /** Call abort for each active drag */
76 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
87 DragManager::add (Drag* d)
89 d->set_manager (this);
94 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
96 assert (_drags.empty ());
97 d->set_manager (this);
103 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
105 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
107 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 (*i)->start_grab (e, c);
112 /** Call end_grab for each active drag.
113 * @return true if any drag reported movement having occurred.
116 DragManager::end_grab (GdkEvent* e)
121 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
122 bool const t = (*i)->end_grab (e);
137 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
141 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->motion_handler (e, from_autoscroll);
155 DragManager::have_item (ArdourCanvas::Item* i) const
157 list<Drag*>::const_iterator j = _drags.begin ();
158 while (j != _drags.end() && (*j)->item () != i) {
162 return j != _drags.end ();
165 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
168 , _pointer_frame_offset (0)
169 , _move_threshold_passed (false)
171 , _last_pointer_frame (0)
177 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
183 cursor = _editor->which_grabber_cursor ();
186 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
190 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
193 cursor = _editor->which_grabber_cursor ();
196 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
198 if (Keyboard::is_button2_event (&event->button)) {
199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
200 _y_constrained = true;
201 _x_constrained = false;
203 _y_constrained = false;
204 _x_constrained = true;
207 _x_constrained = false;
208 _y_constrained = false;
211 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
212 _grab_frame = adjusted_frame (_grab_frame, event);
213 _last_pointer_frame = _grab_frame;
214 _last_pointer_x = _grab_x;
215 _last_pointer_y = _grab_y;
217 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
221 if (_editor->session() && _editor->session()->transport_rolling()) {
224 _was_rolling = false;
227 switch (_editor->snap_type()) {
228 case SnapToRegionStart:
229 case SnapToRegionEnd:
230 case SnapToRegionSync:
231 case SnapToRegionBoundary:
232 _editor->build_region_boundary_cache ();
239 /** Call to end a drag `successfully'. Ungrabs item and calls
240 * subclass' finished() method.
242 * @param event GDK event, or 0.
243 * @return true if some movement occurred, otherwise false.
246 Drag::end_grab (GdkEvent* event)
248 _editor->stop_canvas_autoscroll ();
250 _item->ungrab (event ? event->button.time : 0);
252 finished (event, _move_threshold_passed);
254 _editor->hide_verbose_canvas_cursor();
256 return _move_threshold_passed;
260 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
264 if (f > _pointer_frame_offset) {
265 pos = f - _pointer_frame_offset;
269 _editor->snap_to_with_modifier (pos, event);
276 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
278 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
282 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
284 /* check to see if we have moved in any way that matters since the last motion event */
285 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
286 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
290 pair<nframes64_t, int> const threshold = move_threshold ();
292 bool const old_move_threshold_passed = _move_threshold_passed;
294 if (!from_autoscroll && !_move_threshold_passed) {
296 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
297 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
299 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
302 if (active (_editor->mouse_mode) && _move_threshold_passed) {
304 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
305 if (!from_autoscroll) {
306 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
309 motion (event, _move_threshold_passed != old_move_threshold_passed);
311 _last_pointer_x = _drags->current_pointer_x ();
312 _last_pointer_y = _drags->current_pointer_y ();
313 _last_pointer_frame = adjusted_current_frame (event);
321 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
331 _editor->stop_canvas_autoscroll ();
332 _editor->hide_verbose_canvas_cursor ();
335 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
339 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
340 _views.push_back (DraggingView (*i));
343 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
347 RegionDrag::region_going_away (RegionView* v)
349 list<DraggingView>::iterator i = _views.begin ();
350 while (i != _views.end() && i->view != v) {
354 if (i != _views.end()) {
359 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
360 : RegionDrag (e, i, p, v),
371 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
373 Drag::start_grab (event);
375 _editor->show_verbose_time_cursor (_last_frame_position, 10);
378 RegionMotionDrag::TimeAxisViewSummary
379 RegionMotionDrag::get_time_axis_view_summary ()
381 int32_t children = 0;
382 TimeAxisViewSummary sum;
384 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
386 /* get a bitmask representing the visible tracks */
388 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
389 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
390 TimeAxisView::Children children_list;
392 /* zeroes are audio/MIDI tracks. ones are other types. */
394 if (!rtv->hidden()) {
396 if (!rtv->is_track()) {
397 /* not an audio nor MIDI track */
398 sum.tracks = sum.tracks |= (0x01 << rtv->order());
401 sum.height_list[rtv->order()] = (*i)->current_height();
404 if ((children_list = rtv->get_child_list()).size() > 0) {
405 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
406 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
407 sum.height_list[rtv->order() + children] = (*j)->current_height();
418 RegionMotionDrag::compute_y_delta (
419 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
420 int32_t last_pointer_layer, int32_t current_pointer_layer,
421 TimeAxisViewSummary const & tavs,
422 int32_t* pointer_order_span, int32_t* pointer_layer_span,
423 int32_t* canvas_pointer_order_span
427 *pointer_order_span = 0;
428 *pointer_layer_span = 0;
432 bool clamp_y_axis = false;
434 /* the change in track order between this callback and the last */
435 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
436 /* the change in layer between this callback and the last;
437 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
438 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
440 if (*pointer_order_span != 0) {
442 /* find the actual pointer span, in terms of the number of visible tracks;
443 to do this, we reduce |pointer_order_span| by the number of hidden tracks
446 *canvas_pointer_order_span = *pointer_order_span;
447 if (last_pointer_view->order() >= current_pointer_view->order()) {
448 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
449 if (tavs.height_list[y] == 0) {
450 *canvas_pointer_order_span--;
454 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
455 if (tavs.height_list[y] == 0) {
456 *canvas_pointer_order_span++;
461 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
463 RegionView* rv = i->view;
465 if (rv->region()->locked()) {
469 double ix1, ix2, iy1, iy2;
470 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
471 rv->get_canvas_frame()->i2w (ix1, iy1);
472 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
474 /* get the new trackview for this particular region */
475 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
477 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
479 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
480 as surely this is a per-region thing... */
482 clamp_y_axis = y_movement_disallowed (
483 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
491 } else if (_dest_trackview == current_pointer_view) {
493 if (current_pointer_layer == last_pointer_layer) {
494 /* No movement; clamp */
500 _dest_trackview = current_pointer_view;
501 _dest_layer = current_pointer_layer;
509 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
511 /* compute the amount of pointer motion in frames, and where
512 the region would be if we moved it by that much.
514 *pending_region_position = adjusted_current_frame (event);
516 nframes64_t sync_frame;
517 nframes64_t sync_offset;
520 sync_offset = _primary->region()->sync_offset (sync_dir);
522 /* we don't handle a sync point that lies before zero.
524 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
526 sync_frame = *pending_region_position + (sync_dir*sync_offset);
528 _editor->snap_to_with_modifier (sync_frame, event);
530 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
533 *pending_region_position = _last_frame_position;
536 if (*pending_region_position > max_frames - _primary->region()->length()) {
537 *pending_region_position = _last_frame_position;
542 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
544 /* x movement since last time */
545 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
547 /* total x movement */
548 framecnt_t total_dx = *pending_region_position;
549 if (regions_came_from_canvas()) {
550 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
553 /* check that no regions have gone off the start of the session */
554 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
555 if ((i->view->region()->position() + total_dx) < 0) {
557 *pending_region_position = _last_frame_position;
562 _last_frame_position = *pending_region_position;
569 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
573 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
575 vector<int32_t>::iterator j;
577 /* *pointer* variables reflect things about the pointer; as we may be moving
578 multiple regions, much detail must be computed per-region */
580 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
581 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
582 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
583 is always 0 regardless of what the region's "real" layer is */
584 RouteTimeAxisView* current_pointer_view;
585 layer_t current_pointer_layer;
586 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
590 /* TimeAxisView that we were pointing at last time we entered this method */
591 TimeAxisView const * const last_pointer_view = _dest_trackview;
592 /* the order of the track that we were pointing at last time we entered this method */
593 int32_t const last_pointer_order = last_pointer_view->order ();
594 /* the layer that we were pointing at last time we entered this method */
595 layer_t const last_pointer_layer = _dest_layer;
597 int32_t pointer_order_span;
598 int32_t pointer_layer_span;
599 int32_t canvas_pointer_order_span;
601 bool const clamp_y_axis = compute_y_delta (
602 last_pointer_view, current_pointer_view,
603 last_pointer_layer, current_pointer_layer, tavs,
604 &pointer_order_span, &pointer_layer_span,
605 &canvas_pointer_order_span
608 nframes64_t pending_region_position;
609 double const x_delta = compute_x_delta (event, &pending_region_position);
611 /*************************************************************
613 ************************************************************/
615 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
616 /* haven't reached next snap point, and we're not switching
617 trackviews nor layers. nothing to do.
622 /*************************************************************
624 ************************************************************/
626 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
628 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
630 RegionView* rv = i->view;
632 if (rv->region()->locked()) {
636 /* here we are calculating the y distance from the
637 top of the first track view to the top of the region
638 area of the track view that we're working on */
640 /* this x value is just a dummy value so that we have something
645 /* distance from the top of this track view to the region area
646 of our track view is always 1 */
650 /* convert to world coordinates, ie distance from the top of
653 rv->get_canvas_frame()->i2w (ix1, iy1);
655 /* compensate for the ruler section and the vertical scrollbar position */
656 iy1 += _editor->get_trackview_group_vertical_offset ();
660 // hide any dependent views
662 rv->get_time_axis_view().hide_dependent_views (*rv);
665 reparent to a non scrolling group so that we can keep the
666 region selection above all time axis views.
667 reparenting means we have to move the rv as the two
668 parent groups have different coordinates.
671 rv->get_canvas_group()->property_y() = iy1 - 1;
672 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
674 rv->fake_set_opaque (true);
677 /* current view for this particular region */
678 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
679 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
681 if (pointer_order_span != 0 && !clamp_y_axis) {
683 /* INTER-TRACK MOVEMENT */
685 /* move through the height list to the track that the region is currently on */
686 vector<int32_t>::iterator j = tavs.height_list.begin ();
688 while (j != tavs.height_list.end () && x != rtv->order ()) {
694 int32_t temp_pointer_order_span = canvas_pointer_order_span;
696 if (j != tavs.height_list.end ()) {
698 /* Account for layers in the original and
699 destination tracks. If we're moving around in layers we assume
700 that only one track is involved, so it's ok to use *pointer*
703 StreamView* lv = last_pointer_view->view ();
706 /* move to the top of the last trackview */
707 if (lv->layer_display () == Stacked) {
708 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
711 StreamView* cv = current_pointer_view->view ();
714 /* move to the right layer on the current trackview */
715 if (cv->layer_display () == Stacked) {
716 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
719 /* And for being on a non-topmost layer on the new
722 while (temp_pointer_order_span > 0) {
723 /* we're moving up canvas-wise,
724 so we need to find the next track height
726 if (j != tavs.height_list.begin()) {
730 if (x != last_pointer_order) {
732 ++temp_pointer_order_span;
737 temp_pointer_order_span--;
740 while (temp_pointer_order_span < 0) {
744 if (x != last_pointer_order) {
746 --temp_pointer_order_span;
750 if (j != tavs.height_list.end()) {
754 temp_pointer_order_span++;
758 /* find out where we'll be when we move and set height accordingly */
760 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
761 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
762 rv->set_height (temp_rtv->view()->child_height());
764 /* if you un-comment the following, the region colours will follow
765 the track colours whilst dragging; personally
766 i think this can confuse things, but never mind.
769 //const GdkColor& col (temp_rtv->view->get_region_color());
770 //rv->set_color (const_cast<GdkColor&>(col));
774 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
776 /* INTER-LAYER MOVEMENT in the same track */
777 y_delta = rtv->view()->child_height () * pointer_layer_span;
781 _editor->mouse_brush_insert_region (rv, pending_region_position);
783 rv->move (x_delta, y_delta);
786 } /* foreach region */
788 _total_x_delta += x_delta;
791 _editor->cursor_group->raise_to_top();
794 if (x_delta != 0 && !_brushing) {
795 _editor->show_verbose_time_cursor (_last_frame_position, 10);
800 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
802 if (_copy && first_move) {
803 copy_regions (event);
806 RegionMotionDrag::motion (event, first_move);
810 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
812 if (!movement_occurred) {
817 /* reverse this here so that we have the correct logic to finalize
821 if (Config->get_edit_mode() == Lock) {
822 _x_constrained = !_x_constrained;
825 bool const changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
826 bool const changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
827 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
829 _editor->update_canvas_now ();
834 find_time_axis_views_and_layers (),
843 find_time_axis_views_and_layers (),
853 RegionMoveDrag::finished_copy (
854 map<RegionView*, pair<RouteTimeAxisView*, int> > const & final,
855 bool const changed_position,
856 bool const changed_tracks,
857 framecnt_t const drag_delta
860 RegionSelection new_views;
861 PlaylistSet modified_playlists;
862 list<RegionView*> views_to_delete;
865 /* all changes were made during motion event handlers */
867 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
871 _editor->commit_reversible_command ();
875 if (_x_constrained) {
876 _editor->begin_reversible_command (_("fixed time region copy"));
878 _editor->begin_reversible_command (_("region copy"));
881 /* insert the regions into their new playlists */
882 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
886 if (i->view->region()->locked()) {
890 if (changed_position && !_x_constrained) {
891 where = i->view->region()->position() - drag_delta;
893 where = i->view->region()->position();
896 map<RegionView*, pair<RouteTimeAxisView*, int> >::const_iterator j = final.find (i->view);
897 assert (j != final.end());
899 RegionView* new_view = insert_region_into_playlist (
900 i->view->region(), j->second.first, j->second.second, where, modified_playlists
907 new_views.push_back (new_view);
909 /* we don't need the copied RegionView any more */
910 views_to_delete.push_back (i->view);
913 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
914 because when views are deleted they are automagically removed from _views, which messes
917 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
921 /* If we've created new regions either by copying or moving
922 to a new track, we want to replace the old selection with the new ones
925 if (new_views.size() > 0) {
926 _editor->selection->set (new_views);
929 /* write commands for the accumulated diffs for all our modified playlists */
930 add_stateful_diff_commands_for_playlists (modified_playlists);
932 _editor->commit_reversible_command ();
936 RegionMoveDrag::finished_no_copy (
937 map<RegionView*, pair<RouteTimeAxisView*, int> > const & final,
938 bool const changed_position,
939 bool const changed_tracks,
940 framecnt_t const drag_delta
943 RegionSelection new_views;
944 PlaylistSet modified_playlists;
945 PlaylistSet frozen_playlists;
948 /* all changes were made during motion event handlers */
949 _editor->commit_reversible_command ();
953 if (_x_constrained) {
954 _editor->begin_reversible_command (_("fixed time region drag"));
956 _editor->begin_reversible_command (_("region drag"));
959 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
961 RegionView* rv = i->view;
963 map<RegionView*, pair<RouteTimeAxisView*, int> >::const_iterator j = final.find (rv);
964 assert (j != final.end());
966 RouteTimeAxisView* dest_rtv = j->second.first;
967 layer_t dest_layer = j->second.second;
969 if (rv->region()->locked()) {
976 if (changed_position && !_x_constrained) {
977 where = rv->region()->position() - drag_delta;
979 where = rv->region()->position();
982 if (changed_tracks) {
984 /* insert into new playlist */
986 RegionView* new_view = insert_region_into_playlist (
987 RegionFactory::create (rv->region ()), dest_rtv, dest_layer, where, modified_playlists
995 new_views.push_back (new_view);
997 /* remove from old playlist */
999 /* the region that used to be in the old playlist is not
1000 moved to the new one - we use a copy of it. as a result,
1001 any existing editor for the region should no longer be
1004 rv->hide_region_editor();
1005 rv->fake_set_opaque (false);
1007 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1011 rv->region()->clear_changes ();
1014 motion on the same track. plonk the previously reparented region
1015 back to its original canvas group (its streamview).
1016 No need to do anything for copies as they are fake regions which will be deleted.
1019 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1020 rv->get_canvas_group()->property_y() = i->initial_y;
1021 rv->get_time_axis_view().reveal_dependent_views (*rv);
1023 /* just change the model */
1025 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1027 if (dest_rtv->view()->layer_display() == Stacked) {
1028 rv->region()->set_layer (dest_layer);
1029 rv->region()->set_pending_explicit_relayer (true);
1032 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1034 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1037 playlist->freeze ();
1040 rv->region()->set_position (where, (void*) this);
1042 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1045 if (changed_tracks) {
1047 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1048 was selected in all of them, then removing it from a playlist will have removed all
1049 trace of it from _views (i.e. there were N regions selected, we removed 1,
1050 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1051 corresponding regionview, and _views is now empty).
1053 This could have invalidated any and all iterators into _views.
1055 The heuristic we use here is: if the region selection is empty, break out of the loop
1056 here. if the region selection is not empty, then restart the loop because we know that
1057 we must have removed at least the region(view) we've just been working on as well as any
1058 that we processed on previous iterations.
1060 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1061 we can just iterate.
1065 if (_views.empty()) {
1076 /* If we've created new regions either by copying or moving
1077 to a new track, we want to replace the old selection with the new ones
1080 if (new_views.size() > 0) {
1081 _editor->selection->set (new_views);
1084 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1088 /* write commands for the accumulated diffs for all our modified playlists */
1089 add_stateful_diff_commands_for_playlists (modified_playlists);
1091 _editor->commit_reversible_command ();
1094 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1095 * @param region Region to remove.
1096 * @param playlist playlist To remove from.
1097 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1098 * that clear_changes () is only called once per playlist.
1101 RegionMoveDrag::remove_region_from_playlist (
1102 boost::shared_ptr<Region> region,
1103 boost::shared_ptr<Playlist> playlist,
1104 PlaylistSet& modified_playlists
1107 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1110 playlist->clear_changes ();
1113 playlist->remove_region (region);
1117 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1118 * clearing the playlist's diff history first if necessary.
1119 * @param region Region to insert.
1120 * @param dest_rtv Destination RouteTimeAxisView.
1121 * @param dest_layer Destination layer.
1122 * @param where Destination position.
1123 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1124 * that clear_changes () is only called once per playlist.
1125 * @return New RegionView, or 0 if no insert was performed.
1128 RegionMoveDrag::insert_region_into_playlist (
1129 boost::shared_ptr<Region> region,
1130 RouteTimeAxisView* dest_rtv,
1133 PlaylistSet& modified_playlists
1136 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1137 if (!dest_playlist) {
1141 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1142 _new_region_view = 0;
1143 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1145 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1146 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1148 dest_playlist->clear_changes ();
1151 dest_playlist->add_region (region, where);
1153 if (dest_rtv->view()->layer_display() == Stacked) {
1154 region->set_layer (dest_layer);
1155 region->set_pending_explicit_relayer (true);
1160 assert (_new_region_view);
1162 return _new_region_view;
1166 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1168 _new_region_view = rv;
1172 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1174 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1175 _editor->session()->add_command (new StatefulDiffCommand (*i));
1181 RegionMoveDrag::aborted ()
1185 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1192 RegionMotionDrag::aborted ();
1197 RegionMotionDrag::aborted ()
1199 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1200 RegionView* rv = i->view;
1201 TimeAxisView* tv = &(rv->get_time_axis_view ());
1202 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1204 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1205 rv->get_canvas_group()->property_y() = 0;
1206 rv->get_time_axis_view().reveal_dependent_views (*rv);
1207 rv->fake_set_opaque (false);
1208 rv->move (-_total_x_delta, 0);
1209 rv->set_height (rtv->view()->child_height ());
1212 _editor->update_canvas_now ();
1217 RegionMotionDrag::x_move_allowed () const
1219 if (Config->get_edit_mode() == Lock) {
1220 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1221 return _x_constrained;
1224 return !_x_constrained;
1228 RegionMotionDrag::copy_regions (GdkEvent* event)
1230 /* duplicate the regionview(s) and region(s) */
1232 list<DraggingView> new_regionviews;
1234 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1236 RegionView* rv = i->view;
1237 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1238 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1240 const boost::shared_ptr<const Region> original = rv->region();
1241 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1242 region_copy->set_position (original->position(), this);
1246 boost::shared_ptr<AudioRegion> audioregion_copy
1247 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1249 nrv = new AudioRegionView (*arv, audioregion_copy);
1251 boost::shared_ptr<MidiRegion> midiregion_copy
1252 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1253 nrv = new MidiRegionView (*mrv, midiregion_copy);
1258 nrv->get_canvas_group()->show ();
1259 new_regionviews.push_back (DraggingView (nrv));
1261 /* swap _primary to the copy */
1263 if (rv == _primary) {
1267 /* ..and deselect the one we copied */
1269 rv->set_selected (false);
1272 if (new_regionviews.empty()) {
1276 /* reflect the fact that we are dragging the copies */
1278 _views = new_regionviews;
1280 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1283 sync the canvas to what we think is its current state
1284 without it, the canvas seems to
1285 "forget" to update properly after the upcoming reparent()
1286 ..only if the mouse is in rapid motion at the time of the grab.
1287 something to do with regionview creation taking so long?
1289 _editor->update_canvas_now();
1293 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1295 /* Which trackview is this ? */
1297 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1298 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1299 (*layer) = tvp.second;
1301 if (*tv && (*tv)->layer_display() == Overlaid) {
1305 /* The region motion is only processed if the pointer is over
1309 if (!(*tv) || !(*tv)->is_track()) {
1310 /* To make sure we hide the verbose canvas cursor when the mouse is
1311 not held over and audiotrack.
1313 _editor->hide_verbose_canvas_cursor ();
1320 /** @param new_order New track order.
1321 * @param old_order Old track order.
1322 * @param visible_y_low Lowest visible order.
1323 * @return true if y movement should not happen, otherwise false.
1326 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1328 if (new_order != old_order) {
1330 /* this isn't the pointer track */
1334 /* moving up the canvas */
1335 if ( (new_order - y_span) >= tavs.visible_y_low) {
1339 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1340 int32_t visible_tracks = 0;
1341 while (visible_tracks < y_span ) {
1343 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1344 /* passing through a hidden track */
1349 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1350 /* moving to a non-track; disallow */
1356 /* moving beyond the lowest visible track; disallow */
1360 } else if (y_span < 0) {
1362 /* moving down the canvas */
1363 if ((new_order - y_span) <= tavs.visible_y_high) {
1365 int32_t visible_tracks = 0;
1367 while (visible_tracks > y_span ) {
1370 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1371 /* passing through a hidden track */
1376 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1377 /* moving to a non-track; disallow */
1384 /* moving beyond the highest visible track; disallow */
1391 /* this is the pointer's track */
1393 if ((new_order - y_span) > tavs.visible_y_high) {
1394 /* we will overflow */
1396 } else if ((new_order - y_span) < tavs.visible_y_low) {
1397 /* we will overflow */
1406 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1407 : RegionMotionDrag (e, i, p, v, b),
1410 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1412 _dest_trackview = tv;
1413 if (tv->layer_display() == Overlaid) {
1416 _dest_layer = _primary->region()->layer ();
1420 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1421 if (rtv && rtv->is_track()) {
1422 speed = rtv->track()->speed ();
1425 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1429 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1431 RegionMotionDrag::start_grab (event, c);
1433 _pointer_frame_offset = grab_frame() - _last_frame_position;
1436 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1437 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1439 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1440 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1442 _primary = v->view()->create_region_view (r, false, false);
1444 _primary->get_canvas_group()->show ();
1445 _primary->set_position (pos, 0);
1446 _views.push_back (DraggingView (_primary));
1448 _last_frame_position = pos;
1450 _item = _primary->get_canvas_group ();
1451 _dest_trackview = v;
1452 _dest_layer = _primary->region()->layer ();
1455 map<RegionView*, pair<RouteTimeAxisView*, int> >
1456 RegionMotionDrag::find_time_axis_views_and_layers ()
1458 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1460 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1462 double ix1, ix2, iy1, iy2;
1463 RegionView* rv = i->view;
1464 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1465 rv->get_canvas_frame()->i2w (ix1, iy1);
1466 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1468 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1469 tav[rv] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1477 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1479 _editor->update_canvas_now ();
1481 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1483 RouteTimeAxisView* dest_rtv = final[_primary].first;
1485 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1486 _primary->get_canvas_group()->property_y() = 0;
1488 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1490 _editor->begin_reversible_command (_("insert region"));
1491 playlist->clear_changes ();
1492 playlist->add_region (_primary->region (), _last_frame_position);
1493 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1494 _editor->commit_reversible_command ();
1502 RegionInsertDrag::aborted ()
1509 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1510 : RegionMoveDrag (e, i, p, v, false, false)
1515 struct RegionSelectionByPosition {
1516 bool operator() (RegionView*a, RegionView* b) {
1517 return a->region()->position () < b->region()->position();
1522 RegionSpliceDrag::motion (GdkEvent* event, bool)
1524 RouteTimeAxisView* tv;
1527 if (!check_possible (&tv, &layer)) {
1533 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1539 RegionSelection copy (_editor->selection->regions);
1541 RegionSelectionByPosition cmp;
1544 nframes64_t const pf = adjusted_current_frame (event);
1546 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1548 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1554 boost::shared_ptr<Playlist> playlist;
1556 if ((playlist = atv->playlist()) == 0) {
1560 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1565 if (pf < (*i)->region()->last_frame() + 1) {
1569 if (pf > (*i)->region()->first_frame()) {
1575 playlist->shuffle ((*i)->region(), dir);
1580 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1586 RegionSpliceDrag::aborted ()
1591 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1593 _view (dynamic_cast<MidiTimeAxisView*> (v))
1599 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1602 /* don't use a zero-length region otherwise its region view will be hidden when it is created */
1603 _region = _view->add_region (grab_frame(), 1, false);
1605 framepos_t const f = adjusted_current_frame (event);
1606 if (f < grab_frame()) {
1607 _region->set_position (f, this);
1610 /* again, don't use a zero-length region (see above) */
1611 framecnt_t const len = abs (f - grab_frame ());
1612 _region->set_length (len < 1 ? 1 : len, this);
1617 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1619 if (movement_occurred) {
1620 _editor->commit_reversible_command ();
1625 RegionCreateDrag::aborted ()
1630 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1638 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1640 Gdk::Cursor* cursor;
1641 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1643 Drag::start_grab (event);
1645 region = &cnote->region_view();
1647 double const region_start = region->get_position_pixels();
1648 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1650 if (grab_x() <= middle_point) {
1651 cursor = _editor->left_side_trim_cursor;
1654 cursor = _editor->right_side_trim_cursor;
1658 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1660 if (event->motion.state & Keyboard::PrimaryModifier) {
1666 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1668 if (ms.size() > 1) {
1669 /* has to be relative, may make no sense otherwise */
1673 /* select this note; if it is already selected, preserve the existing selection,
1674 otherwise make this note the only one selected.
1676 region->note_selected (cnote, cnote->selected ());
1678 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1679 MidiRegionSelection::iterator next;
1682 (*r)->begin_resizing (at_front);
1688 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1690 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1691 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1692 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1697 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1699 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1700 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1701 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1706 NoteResizeDrag::aborted ()
1712 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1718 RegionGainDrag::finished (GdkEvent *, bool)
1724 RegionGainDrag::aborted ()
1729 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1730 : RegionDrag (e, i, p, v)
1731 , _have_transaction (false)
1737 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1740 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1741 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1743 if (tv && tv->is_track()) {
1744 speed = tv->track()->speed();
1747 nframes64_t const region_start = (nframes64_t) (_primary->region()->position() / speed);
1748 nframes64_t const region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1749 nframes64_t const region_length = (nframes64_t) (_primary->region()->length() / speed);
1751 nframes64_t const pf = adjusted_current_frame (event);
1753 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1754 _operation = ContentsTrim;
1755 Drag::start_grab (event, _editor->trimmer_cursor);
1757 /* These will get overridden for a point trim.*/
1758 if (pf < (region_start + region_length/2)) {
1759 /* closer to start */
1760 _operation = StartTrim;
1761 Drag::start_grab (event, _editor->left_side_trim_cursor);
1764 _operation = EndTrim;
1765 Drag::start_grab (event, _editor->right_side_trim_cursor);
1769 switch (_operation) {
1771 _editor->show_verbose_time_cursor (region_start, 10);
1774 _editor->show_verbose_time_cursor (region_end, 10);
1777 _editor->show_verbose_time_cursor (pf, 10);
1783 TrimDrag::motion (GdkEvent* event, bool first_move)
1785 RegionView* rv = _primary;
1787 /* snap modifier works differently here..
1788 its current state has to be passed to the
1789 various trim functions in order to work properly
1793 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1794 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1795 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1797 if (tv && tv->is_track()) {
1798 speed = tv->track()->speed();
1801 nframes64_t const pf = adjusted_current_frame (event);
1807 switch (_operation) {
1809 trim_type = "Region start trim";
1812 trim_type = "Region end trim";
1815 trim_type = "Region content trim";
1819 _editor->begin_reversible_command (trim_type);
1820 _have_transaction = true;
1822 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1823 RegionView* rv = i->view;
1824 rv->fake_set_opaque(false);
1825 rv->enable_display (false);
1826 rv->region()->clear_changes ();
1827 rv->region()->suspend_property_changes ();
1829 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1832 arv->temporarily_hide_envelope ();
1835 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1836 insert_result = _editor->motion_frozen_playlists.insert (pl);
1838 if (insert_result.second) {
1844 bool non_overlap_trim = false;
1846 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1847 non_overlap_trim = true;
1850 switch (_operation) {
1852 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1853 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1858 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1859 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1865 bool swap_direction = false;
1867 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1868 swap_direction = true;
1871 nframes64_t frame_delta = 0;
1873 bool left_direction = false;
1874 if (last_pointer_frame() > pf) {
1875 left_direction = true;
1878 if (left_direction) {
1879 frame_delta = (last_pointer_frame() - pf);
1881 frame_delta = (pf - last_pointer_frame());
1884 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1885 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1891 switch (_operation) {
1893 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1896 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1899 _editor->show_verbose_time_cursor (pf, 10);
1906 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1908 if (movement_occurred) {
1909 motion (event, false);
1911 if (!_editor->selection->selected (_primary)) {
1912 _editor->thaw_region_after_trim (*_primary);
1915 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1916 _editor->thaw_region_after_trim (*i->view);
1917 i->view->enable_display (true);
1918 i->view->fake_set_opaque (true);
1919 if (_have_transaction) {
1920 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1924 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1928 _editor->motion_frozen_playlists.clear ();
1930 if (_have_transaction) {
1931 _editor->commit_reversible_command();
1935 /* no mouse movement */
1936 _editor->point_trim (event, adjusted_current_frame (event));
1941 TrimDrag::aborted ()
1943 /* Our motion method is changing model state, so use the Undo system
1944 to cancel. Perhaps not ideal, as this will leave an Undo point
1945 behind which may be slightly odd from the user's point of view.
1950 if (_have_transaction) {
1955 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1959 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1964 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1967 // create a dummy marker for visual representation of moving the copy.
1968 // The actual copying is not done before we reach the finish callback.
1970 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1971 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1972 *new MeterSection (_marker->meter()));
1974 _item = &new_marker->the_item ();
1975 _marker = new_marker;
1979 MetricSection& section (_marker->meter());
1981 if (!section.movable()) {
1987 Drag::start_grab (event, cursor);
1989 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1991 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1995 MeterMarkerDrag::motion (GdkEvent* event, bool)
1997 nframes64_t const pf = adjusted_current_frame (event);
1999 _marker->set_position (pf);
2001 _editor->show_verbose_time_cursor (pf, 10);
2005 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2007 if (!movement_occurred) {
2011 motion (event, false);
2015 TempoMap& map (_editor->session()->tempo_map());
2016 map.bbt_time (last_pointer_frame(), when);
2018 if (_copy == true) {
2019 _editor->begin_reversible_command (_("copy meter mark"));
2020 XMLNode &before = map.get_state();
2021 map.add_meter (_marker->meter(), when);
2022 XMLNode &after = map.get_state();
2023 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2024 _editor->commit_reversible_command ();
2026 // delete the dummy marker we used for visual representation of copying.
2027 // a new visual marker will show up automatically.
2030 _editor->begin_reversible_command (_("move meter mark"));
2031 XMLNode &before = map.get_state();
2032 map.move_meter (_marker->meter(), when);
2033 XMLNode &after = map.get_state();
2034 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2035 _editor->commit_reversible_command ();
2040 MeterMarkerDrag::aborted ()
2042 _marker->set_position (_marker->meter().frame ());
2045 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2049 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2054 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2059 // create a dummy marker for visual representation of moving the copy.
2060 // The actual copying is not done before we reach the finish callback.
2062 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2063 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2064 *new TempoSection (_marker->tempo()));
2066 _item = &new_marker->the_item ();
2067 _marker = new_marker;
2071 MetricSection& section (_marker->tempo());
2073 if (!section.movable()) {
2078 Drag::start_grab (event, cursor);
2080 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2081 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2085 TempoMarkerDrag::motion (GdkEvent* event, bool)
2087 nframes64_t const pf = adjusted_current_frame (event);
2088 _marker->set_position (pf);
2089 _editor->show_verbose_time_cursor (pf, 10);
2093 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2095 if (!movement_occurred) {
2099 motion (event, false);
2103 TempoMap& map (_editor->session()->tempo_map());
2104 map.bbt_time (last_pointer_frame(), when);
2106 if (_copy == true) {
2107 _editor->begin_reversible_command (_("copy tempo mark"));
2108 XMLNode &before = map.get_state();
2109 map.add_tempo (_marker->tempo(), when);
2110 XMLNode &after = map.get_state();
2111 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2112 _editor->commit_reversible_command ();
2114 // delete the dummy marker we used for visual representation of copying.
2115 // a new visual marker will show up automatically.
2118 _editor->begin_reversible_command (_("move tempo mark"));
2119 XMLNode &before = map.get_state();
2120 map.move_tempo (_marker->tempo(), when);
2121 XMLNode &after = map.get_state();
2122 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2123 _editor->commit_reversible_command ();
2128 TempoMarkerDrag::aborted ()
2130 _marker->set_position (_marker->tempo().frame());
2133 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2137 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2142 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2144 Drag::start_grab (event, c);
2148 nframes64_t where = _editor->event_frame (event, 0, 0);
2150 _editor->snap_to_with_modifier (where, event);
2151 _editor->playhead_cursor->set_position (where);
2155 if (_cursor == _editor->playhead_cursor) {
2156 _editor->_dragging_playhead = true;
2158 Session* s = _editor->session ();
2161 if (_was_rolling && _stop) {
2165 if (s->is_auditioning()) {
2166 s->cancel_audition ();
2169 s->request_suspend_timecode_transmission ();
2171 if (s->timecode_transmission_suspended ()) {
2172 nframes64_t const f = _editor->playhead_cursor->current_frame;
2173 s->send_mmc_locate (f);
2174 s->send_full_time_code (f);
2179 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2181 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2185 CursorDrag::motion (GdkEvent* event, bool)
2187 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2189 if (adjusted_frame == last_pointer_frame()) {
2193 _cursor->set_position (adjusted_frame);
2195 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2197 Session* s = _editor->session ();
2198 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2199 nframes64_t const f = _editor->playhead_cursor->current_frame;
2200 s->send_mmc_locate (f);
2201 s->send_full_time_code (f);
2206 _editor->update_canvas_now ();
2208 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2212 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2214 _editor->_dragging_playhead = false;
2216 if (!movement_occurred && _stop) {
2220 motion (event, false);
2222 if (_item == &_editor->playhead_cursor->canvas_item) {
2223 Session* s = _editor->session ();
2225 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2226 _editor->_pending_locate_request = true;
2227 s->request_resume_timecode_transmission ();
2233 CursorDrag::aborted ()
2235 if (_editor->_dragging_playhead) {
2236 _editor->session()->request_resume_timecode_transmission ();
2237 _editor->_dragging_playhead = false;
2240 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2243 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2244 : RegionDrag (e, i, p, v)
2250 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2252 Drag::start_grab (event, cursor);
2254 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2255 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2257 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2258 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2263 FadeInDrag::motion (GdkEvent* event, bool)
2265 nframes64_t fade_length;
2267 nframes64_t const pos = adjusted_current_frame (event);
2269 boost::shared_ptr<Region> region = _primary->region ();
2271 if (pos < (region->position() + 64)) {
2272 fade_length = 64; // this should be a minimum defined somewhere
2273 } else if (pos > region->last_frame()) {
2274 fade_length = region->length();
2276 fade_length = pos - region->position();
2279 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2281 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2287 tmp->reset_fade_in_shape_width (fade_length);
2290 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2294 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2296 if (!movement_occurred) {
2300 nframes64_t fade_length;
2302 nframes64_t const pos = adjusted_current_frame (event);
2304 boost::shared_ptr<Region> region = _primary->region ();
2306 if (pos < (region->position() + 64)) {
2307 fade_length = 64; // this should be a minimum defined somewhere
2308 } else if (pos > region->last_frame()) {
2309 fade_length = region->length();
2311 fade_length = pos - region->position();
2314 _editor->begin_reversible_command (_("change fade in length"));
2316 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2318 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2324 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2325 XMLNode &before = alist->get_state();
2327 tmp->audio_region()->set_fade_in_length (fade_length);
2328 tmp->audio_region()->set_fade_in_active (true);
2330 XMLNode &after = alist->get_state();
2331 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2334 _editor->commit_reversible_command ();
2338 FadeInDrag::aborted ()
2340 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2341 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2347 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2351 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2352 : RegionDrag (e, i, p, v)
2358 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2360 Drag::start_grab (event, cursor);
2362 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2363 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2365 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2366 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2370 FadeOutDrag::motion (GdkEvent* event, bool)
2372 nframes64_t fade_length;
2374 nframes64_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);
2399 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2403 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2405 if (!movement_occurred) {
2409 nframes64_t fade_length;
2411 nframes64_t const pos = adjusted_current_frame (event);
2413 boost::shared_ptr<Region> region = _primary->region ();
2415 if (pos > (region->last_frame() - 64)) {
2416 fade_length = 64; // this should really be a minimum fade defined somewhere
2418 else if (pos < region->position()) {
2419 fade_length = region->length();
2422 fade_length = region->last_frame() - pos;
2425 _editor->begin_reversible_command (_("change fade out length"));
2427 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2429 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2435 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2436 XMLNode &before = alist->get_state();
2438 tmp->audio_region()->set_fade_out_length (fade_length);
2439 tmp->audio_region()->set_fade_out_active (true);
2441 XMLNode &after = alist->get_state();
2442 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2445 _editor->commit_reversible_command ();
2449 FadeOutDrag::aborted ()
2451 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2452 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2458 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2462 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2465 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2468 _points.push_back (Gnome::Art::Point (0, 0));
2469 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2471 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2472 _line->property_width_pixels() = 1;
2473 _line->property_points () = _points;
2476 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2479 MarkerDrag::~MarkerDrag ()
2481 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2487 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2489 Drag::start_grab (event, cursor);
2493 Location *location = _editor->find_location_from_marker (_marker, is_start);
2494 _editor->_dragging_edit_point = true;
2496 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2498 update_item (location);
2500 // _drag_line->show();
2501 // _line->raise_to_top();
2504 _editor->show_verbose_time_cursor (location->start(), 10);
2506 _editor->show_verbose_time_cursor (location->end(), 10);
2509 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2512 case Selection::Toggle:
2513 _editor->selection->toggle (_marker);
2515 case Selection::Set:
2516 if (!_editor->selection->selected (_marker)) {
2517 _editor->selection->set (_marker);
2520 case Selection::Extend:
2522 Locations::LocationList ll;
2523 list<Marker*> to_add;
2525 _editor->selection->markers.range (s, e);
2526 s = min (_marker->position(), s);
2527 e = max (_marker->position(), e);
2530 if (e < max_frames) {
2533 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2534 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2535 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2538 to_add.push_back (lm->start);
2541 to_add.push_back (lm->end);
2545 if (!to_add.empty()) {
2546 _editor->selection->add (to_add);
2550 case Selection::Add:
2551 _editor->selection->add (_marker);
2555 /* Set up copies for us to manipulate during the drag */
2557 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2558 Location* l = _editor->find_location_from_marker (*i, is_start);
2559 _copied_locations.push_back (new Location (*l));
2564 MarkerDrag::motion (GdkEvent* event, bool)
2566 nframes64_t f_delta = 0;
2568 bool move_both = false;
2570 Location *real_location;
2571 Location *copy_location = 0;
2573 nframes64_t const newframe = adjusted_current_frame (event);
2575 nframes64_t next = newframe;
2577 if (newframe == last_pointer_frame()) {
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()) {
2611 case Marker::LoopStart:
2612 case Marker::PunchIn:
2613 f_delta = newframe - copy_location->start();
2617 case Marker::LoopEnd:
2618 case Marker::PunchOut:
2619 f_delta = newframe - copy_location->end();
2622 /* what kind of marker is this ? */
2630 if (i == _editor->selection->markers.end()) {
2631 /* hmm, impossible - we didn't find the dragged marker */
2635 /* now move them all */
2637 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2638 x != _copied_locations.end() && i != _editor->selection->markers.end();
2644 /* call this to find out if its the start or end */
2646 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2650 if (real_location->locked()) {
2654 if (copy_location->is_mark()) {
2658 copy_location->set_start (copy_location->start() + f_delta);
2662 nframes64_t new_start = copy_location->start() + f_delta;
2663 nframes64_t new_end = copy_location->end() + f_delta;
2665 if (is_start) { // start-of-range marker
2668 copy_location->set_start (new_start);
2669 copy_location->set_end (new_end);
2670 } else if (new_start < copy_location->end()) {
2671 copy_location->set_start (new_start);
2673 _editor->snap_to (next, 1, true);
2674 copy_location->set_end (next);
2675 copy_location->set_start (newframe);
2678 } else { // end marker
2681 copy_location->set_end (new_end);
2682 copy_location->set_start (new_start);
2683 } else if (new_end > copy_location->start()) {
2684 copy_location->set_end (new_end);
2685 } else if (newframe > 0) {
2686 _editor->snap_to (next, -1, true);
2687 copy_location->set_start (next);
2688 copy_location->set_end (newframe);
2693 update_item (copy_location);
2695 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2698 lm->set_position (copy_location->start(), copy_location->end());
2702 assert (!_copied_locations.empty());
2704 _editor->show_verbose_time_cursor (newframe, 10);
2707 _editor->update_canvas_now ();
2712 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2714 if (!movement_occurred) {
2716 /* just a click, do nothing but finish
2717 off the selection process
2720 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2723 case Selection::Set:
2724 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2725 _editor->selection->set (_marker);
2729 case Selection::Toggle:
2730 case Selection::Extend:
2731 case Selection::Add:
2738 _editor->_dragging_edit_point = false;
2740 _editor->begin_reversible_command ( _("move marker") );
2741 XMLNode &before = _editor->session()->locations()->get_state();
2743 MarkerSelection::iterator i;
2744 list<Location*>::iterator x;
2747 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2748 x != _copied_locations.end() && i != _editor->selection->markers.end();
2751 Location * location = _editor->find_location_from_marker (*i, is_start);
2755 if (location->locked()) {
2759 if (location->is_mark()) {
2760 location->set_start ((*x)->start());
2762 location->set ((*x)->start(), (*x)->end());
2767 XMLNode &after = _editor->session()->locations()->get_state();
2768 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2769 _editor->commit_reversible_command ();
2775 MarkerDrag::aborted ()
2781 MarkerDrag::update_item (Location* location)
2783 double const x1 = _editor->frame_to_pixel (location->start());
2785 _points.front().set_x(x1);
2786 _points.back().set_x(x1);
2787 _line->property_points() = _points;
2790 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2792 _cumulative_x_drag (0),
2793 _cumulative_y_drag (0)
2795 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2801 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2803 Drag::start_grab (event, _editor->fader_cursor);
2805 // start the grab at the center of the control point so
2806 // the point doesn't 'jump' to the mouse after the first drag
2807 _fixed_grab_x = _point->get_x();
2808 _fixed_grab_y = _point->get_y();
2810 float const fraction = 1 - (_point->get_y() / _point->line().height());
2812 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2814 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2815 event->button.x + 10, event->button.y + 10);
2817 _editor->show_verbose_canvas_cursor ();
2821 ControlPointDrag::motion (GdkEvent* event, bool)
2823 double dx = _drags->current_pointer_x() - last_pointer_x();
2824 double dy = _drags->current_pointer_y() - last_pointer_y();
2826 if (event->button.state & Keyboard::SecondaryModifier) {
2831 /* coordinate in pixels relative to the start of the region (for region-based automation)
2832 or track (for track-based automation) */
2833 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2834 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2836 // calculate zero crossing point. back off by .01 to stay on the
2837 // positive side of zero
2838 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2840 // make sure we hit zero when passing through
2841 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2845 if (_x_constrained) {
2848 if (_y_constrained) {
2852 _cumulative_x_drag = cx - _fixed_grab_x;
2853 _cumulative_y_drag = cy - _fixed_grab_y;
2857 cy = min ((double) _point->line().height(), cy);
2859 framepos_t cx_frames = _editor->unit_to_frame (cx);
2861 if (!_x_constrained) {
2862 _editor->snap_to_with_modifier (cx_frames, event);
2865 cx_frames = min (cx_frames, _point->line().maximum_time());
2867 float const fraction = 1.0 - (cy / _point->line().height());
2869 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2871 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2873 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2877 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2879 if (!movement_occurred) {
2883 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2884 _editor->reset_point_selection ();
2888 motion (event, false);
2890 _point->line().end_drag ();
2894 ControlPointDrag::aborted ()
2896 _point->line().reset ();
2900 ControlPointDrag::active (Editing::MouseMode m)
2902 if (m == Editing::MouseGain) {
2903 /* always active in mouse gain */
2907 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2908 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2911 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2914 _cumulative_y_drag (0)
2919 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2921 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2924 _item = &_line->grab_item ();
2926 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2927 origin, and ditto for y.
2930 double cx = event->button.x;
2931 double cy = event->button.y;
2933 _line->parent_group().w2i (cx, cy);
2935 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2940 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2941 /* no adjacent points */
2945 Drag::start_grab (event, _editor->fader_cursor);
2947 /* store grab start in parent frame */
2952 double fraction = 1.0 - (cy / _line->height());
2954 _line->start_drag_line (before, after, fraction);
2956 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2957 event->button.x + 10, event->button.y + 10);
2959 _editor->show_verbose_canvas_cursor ();
2963 LineDrag::motion (GdkEvent* event, bool)
2965 double dy = _drags->current_pointer_y() - last_pointer_y();
2967 if (event->button.state & Keyboard::SecondaryModifier) {
2971 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2973 _cumulative_y_drag = cy - _fixed_grab_y;
2976 cy = min ((double) _line->height(), cy);
2978 double const fraction = 1.0 - (cy / _line->height());
2982 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2988 /* we are ignoring x position for this drag, so we can just pass in anything */
2989 _line->drag_motion (0, fraction, true, push);
2991 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2995 LineDrag::finished (GdkEvent* event, bool)
2997 motion (event, false);
3002 LineDrag::aborted ()
3007 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3010 _cumulative_x_drag (0)
3015 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3018 Drag::start_grab (event);
3020 _line = reinterpret_cast<SimpleLine*> (_item);
3023 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3025 double cx = event->button.x;
3026 double cy = event->button.y;
3028 _item->property_parent().get_value()->w2i(cx, cy);
3030 /* store grab start in parent frame */
3031 _region_view_grab_x = cx;
3033 _before = _line->property_x1();
3035 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3037 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3041 FeatureLineDrag::motion (GdkEvent* event, bool)
3043 double dx = _drags->current_pointer_x() - last_pointer_x();
3045 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3047 _cumulative_x_drag += dx;
3049 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3058 _line->property_x1() = cx;
3059 _line->property_x2() = cx;
3061 _before = _line->property_x1();
3065 FeatureLineDrag::finished (GdkEvent* event, bool)
3067 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3068 _arv->update_transient(_before, _line->property_x1());
3072 FeatureLineDrag::aborted ()
3078 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3080 Drag::start_grab (event);
3081 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3085 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3092 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3094 nframes64_t grab = grab_frame ();
3095 if (Config->get_rubberbanding_snaps_to_grid ()) {
3096 _editor->snap_to_with_modifier (grab, event);
3099 /* base start and end on initial click position */
3109 if (_drags->current_pointer_y() < grab_y()) {
3110 y1 = _drags->current_pointer_y();
3113 y2 = _drags->current_pointer_y();
3118 if (start != end || y1 != y2) {
3120 double x1 = _editor->frame_to_pixel (start);
3121 double x2 = _editor->frame_to_pixel (end);
3123 _editor->rubberband_rect->property_x1() = x1;
3124 _editor->rubberband_rect->property_y1() = y1;
3125 _editor->rubberband_rect->property_x2() = x2;
3126 _editor->rubberband_rect->property_y2() = y2;
3128 _editor->rubberband_rect->show();
3129 _editor->rubberband_rect->raise_to_top();
3131 _editor->show_verbose_time_cursor (pf, 10);
3136 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3138 if (movement_occurred) {
3140 motion (event, false);
3143 if (_drags->current_pointer_y() < grab_y()) {
3144 y1 = _drags->current_pointer_y();
3147 y2 = _drags->current_pointer_y();
3152 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3155 _editor->begin_reversible_command (_("rubberband selection"));
3157 if (grab_frame() < last_pointer_frame()) {
3158 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3160 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3164 _editor->commit_reversible_command ();
3168 if (!getenv("ARDOUR_SAE")) {
3169 _editor->selection->clear_tracks();
3171 _editor->selection->clear_regions();
3172 _editor->selection->clear_points ();
3173 _editor->selection->clear_lines ();
3176 _editor->rubberband_rect->hide();
3180 RubberbandSelectDrag::aborted ()
3182 _editor->rubberband_rect->hide ();
3186 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3188 Drag::start_grab (event);
3190 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3194 TimeFXDrag::motion (GdkEvent* event, bool)
3196 RegionView* rv = _primary;
3198 nframes64_t const pf = adjusted_current_frame (event);
3200 if (pf > rv->region()->position()) {
3201 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3204 _editor->show_verbose_time_cursor (pf, 10);
3208 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3210 _primary->get_time_axis_view().hide_timestretch ();
3212 if (!movement_occurred) {
3216 if (last_pointer_frame() < _primary->region()->position()) {
3217 /* backwards drag of the left edge - not usable */
3221 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3223 float percentage = (double) newlen / (double) _primary->region()->length();
3225 #ifndef USE_RUBBERBAND
3226 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3227 if (_primary->region()->data_type() == DataType::AUDIO) {
3228 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3232 _editor->begin_reversible_command (_("timestretch"));
3234 // XXX how do timeFX on multiple regions ?
3239 if (_editor->time_stretch (rs, percentage) == -1) {
3240 error << _("An error occurred while executing time stretch operation") << endmsg;
3245 TimeFXDrag::aborted ()
3247 _primary->get_time_axis_view().hide_timestretch ();
3252 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3254 Drag::start_grab (event);
3258 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3260 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3264 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3266 if (movement_occurred && _editor->session()) {
3267 /* make sure we stop */
3268 _editor->session()->request_transport_speed (0.0);
3273 ScrubDrag::aborted ()
3278 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3282 , _original_pointer_time_axis (-1)
3283 , _last_pointer_time_axis (-1)
3289 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3291 nframes64_t start = 0;
3292 nframes64_t end = 0;
3294 if (_editor->session() == 0) {
3298 Gdk::Cursor* cursor = 0;
3300 switch (_operation) {
3301 case CreateSelection:
3302 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3307 cursor = _editor->selector_cursor;
3308 Drag::start_grab (event, cursor);
3311 case SelectionStartTrim:
3312 if (_editor->clicked_axisview) {
3313 _editor->clicked_axisview->order_selection_trims (_item, true);
3315 Drag::start_grab (event, _editor->left_side_trim_cursor);
3316 start = _editor->selection->time[_editor->clicked_selection].start;
3317 _pointer_frame_offset = grab_frame() - start;
3320 case SelectionEndTrim:
3321 if (_editor->clicked_axisview) {
3322 _editor->clicked_axisview->order_selection_trims (_item, false);
3324 Drag::start_grab (event, _editor->right_side_trim_cursor);
3325 end = _editor->selection->time[_editor->clicked_selection].end;
3326 _pointer_frame_offset = grab_frame() - end;
3330 start = _editor->selection->time[_editor->clicked_selection].start;
3331 Drag::start_grab (event, cursor);
3332 _pointer_frame_offset = grab_frame() - start;
3336 if (_operation == SelectionMove) {
3337 _editor->show_verbose_time_cursor (start, 10);
3339 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3342 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3346 SelectionDrag::motion (GdkEvent* event, bool first_move)
3348 nframes64_t start = 0;
3349 nframes64_t end = 0;
3352 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3353 if (pending_time_axis.first == 0) {
3357 nframes64_t const pending_position = adjusted_current_frame (event);
3359 /* only alter selection if things have changed */
3361 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3365 switch (_operation) {
3366 case CreateSelection:
3368 nframes64_t grab = grab_frame ();
3371 _editor->snap_to (grab);
3374 if (pending_position < grab_frame()) {
3375 start = pending_position;
3378 end = pending_position;
3382 /* first drag: Either add to the selection
3383 or create a new selection
3389 /* adding to the selection */
3390 _editor->selection->add (_editor->clicked_axisview);
3391 _editor->clicked_selection = _editor->selection->add (start, end);
3396 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3397 _editor->selection->set (_editor->clicked_axisview);
3400 _editor->clicked_selection = _editor->selection->set (start, end);
3404 /* select the track that we're in */
3405 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3406 _editor->selection->add (pending_time_axis.first);
3407 _added_time_axes.push_back (pending_time_axis.first);
3410 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3411 tracks that we selected in the first place.
3414 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3415 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3417 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3418 while (i != _added_time_axes.end()) {
3420 list<TimeAxisView*>::iterator tmp = i;
3423 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3424 _editor->selection->remove (*i);
3425 _added_time_axes.remove (*i);
3434 case SelectionStartTrim:
3436 start = _editor->selection->time[_editor->clicked_selection].start;
3437 end = _editor->selection->time[_editor->clicked_selection].end;
3439 if (pending_position > end) {
3442 start = pending_position;
3446 case SelectionEndTrim:
3448 start = _editor->selection->time[_editor->clicked_selection].start;
3449 end = _editor->selection->time[_editor->clicked_selection].end;
3451 if (pending_position < start) {
3454 end = pending_position;
3461 start = _editor->selection->time[_editor->clicked_selection].start;
3462 end = _editor->selection->time[_editor->clicked_selection].end;
3464 length = end - start;
3466 start = pending_position;
3467 _editor->snap_to (start);
3469 end = start + length;
3474 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3475 _editor->start_canvas_autoscroll (1, 0);
3479 _editor->selection->replace (_editor->clicked_selection, start, end);
3482 if (_operation == SelectionMove) {
3483 _editor->show_verbose_time_cursor(start, 10);
3485 _editor->show_verbose_time_cursor(pending_position, 10);
3490 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3492 Session* s = _editor->session();
3494 if (movement_occurred) {
3495 motion (event, false);
3496 /* XXX this is not object-oriented programming at all. ick */
3497 if (_editor->selection->time.consolidate()) {
3498 _editor->selection->TimeChanged ();
3501 /* XXX what if its a music time selection? */
3502 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3503 s->request_play_range (&_editor->selection->time, true);
3508 /* just a click, no pointer movement.*/
3510 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3511 _editor->selection->clear_time();
3514 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3515 _editor->selection->set (_editor->clicked_axisview);
3518 if (s && s->get_play_range () && s->transport_rolling()) {
3519 s->request_stop (false, false);
3524 _editor->stop_canvas_autoscroll ();
3528 SelectionDrag::aborted ()
3533 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3538 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3539 physical_screen_height (_editor->get_window()));
3540 _drag_rect->hide ();
3542 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3543 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3547 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3549 if (_editor->session() == 0) {
3553 Gdk::Cursor* cursor = 0;
3555 if (!_editor->temp_location) {
3556 _editor->temp_location = new Location (*_editor->session());
3559 switch (_operation) {
3560 case CreateRangeMarker:
3561 case CreateTransportMarker:
3562 case CreateCDMarker:
3564 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3569 cursor = _editor->selector_cursor;
3573 Drag::start_grab (event, cursor);
3575 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3579 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3581 nframes64_t start = 0;
3582 nframes64_t end = 0;
3583 ArdourCanvas::SimpleRect *crect;
3585 switch (_operation) {
3586 case CreateRangeMarker:
3587 crect = _editor->range_bar_drag_rect;
3589 case CreateTransportMarker:
3590 crect = _editor->transport_bar_drag_rect;
3592 case CreateCDMarker:
3593 crect = _editor->cd_marker_bar_drag_rect;
3596 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3601 nframes64_t const pf = adjusted_current_frame (event);
3603 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3604 nframes64_t grab = grab_frame ();
3605 _editor->snap_to (grab);
3607 if (pf < grab_frame()) {
3615 /* first drag: Either add to the selection
3616 or create a new selection.
3621 _editor->temp_location->set (start, end);
3625 update_item (_editor->temp_location);
3627 //_drag_rect->raise_to_top();
3632 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3633 _editor->start_canvas_autoscroll (1, 0);
3637 _editor->temp_location->set (start, end);
3639 double x1 = _editor->frame_to_pixel (start);
3640 double x2 = _editor->frame_to_pixel (end);
3641 crect->property_x1() = x1;
3642 crect->property_x2() = x2;
3644 update_item (_editor->temp_location);
3647 _editor->show_verbose_time_cursor (pf, 10);
3652 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3654 Location * newloc = 0;
3658 if (movement_occurred) {
3659 motion (event, false);
3662 switch (_operation) {
3663 case CreateRangeMarker:
3664 case CreateCDMarker:
3666 _editor->begin_reversible_command (_("new range marker"));
3667 XMLNode &before = _editor->session()->locations()->get_state();
3668 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3669 if (_operation == CreateCDMarker) {
3670 flags = Location::IsRangeMarker | Location::IsCDMarker;
3671 _editor->cd_marker_bar_drag_rect->hide();
3674 flags = Location::IsRangeMarker;
3675 _editor->range_bar_drag_rect->hide();
3677 newloc = new Location (
3678 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3681 _editor->session()->locations()->add (newloc, true);
3682 XMLNode &after = _editor->session()->locations()->get_state();
3683 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3684 _editor->commit_reversible_command ();
3688 case CreateTransportMarker:
3689 // popup menu to pick loop or punch
3690 _editor->new_transport_marker_context_menu (&event->button, _item);
3694 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3696 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3701 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3703 if (end == max_frames) {
3704 end = _editor->session()->current_end_frame ();
3707 if (start == max_frames) {
3708 start = _editor->session()->current_start_frame ();
3711 switch (_editor->mouse_mode) {
3713 /* find the two markers on either side and then make the selection from it */
3714 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3718 /* find the two markers on either side of the click and make the range out of it */
3719 _editor->selection->set (start, end);
3728 _editor->stop_canvas_autoscroll ();
3732 RangeMarkerBarDrag::aborted ()
3738 RangeMarkerBarDrag::update_item (Location* location)
3740 double const x1 = _editor->frame_to_pixel (location->start());
3741 double const x2 = _editor->frame_to_pixel (location->end());
3743 _drag_rect->property_x1() = x1;
3744 _drag_rect->property_x2() = x2;
3748 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3750 Drag::start_grab (event, _editor->zoom_cursor);
3751 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3755 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3760 nframes64_t const pf = adjusted_current_frame (event);
3762 nframes64_t grab = grab_frame ();
3763 _editor->snap_to_with_modifier (grab, event);
3765 /* base start and end on initial click position */
3777 _editor->zoom_rect->show();
3778 _editor->zoom_rect->raise_to_top();
3781 _editor->reposition_zoom_rect(start, end);
3783 _editor->show_verbose_time_cursor (pf, 10);
3788 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3790 if (movement_occurred) {
3791 motion (event, false);
3793 if (grab_frame() < last_pointer_frame()) {
3794 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3796 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3799 _editor->temporal_zoom_to_frame (false, grab_frame());
3801 temporal_zoom_step (false);
3802 center_screen (grab_frame());
3806 _editor->zoom_rect->hide();
3810 MouseZoomDrag::aborted ()
3812 _editor->zoom_rect->hide ();
3815 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3817 , _cumulative_dx (0)
3818 , _cumulative_dy (0)
3820 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3821 _region = &_primary->region_view ();
3822 _note_height = _region->midi_stream_view()->note_height ();
3826 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3828 Drag::start_grab (event);
3830 if (!(_was_selected = _primary->selected())) {
3832 /* tertiary-click means extend selection - we'll do that on button release,
3833 so don't add it here, because otherwise we make it hard to figure
3834 out the "extend-to" range.
3837 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3840 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3843 _region->note_selected (_primary, true);
3845 _region->unique_select (_primary);
3851 /** @return Current total drag x change in frames */
3853 NoteDrag::total_dx () const
3856 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3858 /* primary note time */
3859 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3861 /* new time of the primary note relative to the region position */
3862 frameoffset_t const st = n + dx;
3864 /* snap and return corresponding delta */
3865 return _region->snap_frame_to_frame (st) - n;
3868 /** @return Current total drag y change in notes */
3870 NoteDrag::total_dy () const
3872 /* this is `backwards' to make increasing note number go in the right direction */
3873 double const dy = _drags->current_pointer_y() - grab_y();
3878 if (abs (dy) >= _note_height) {
3880 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3882 ndy = (int8_t) floor (dy / _note_height / 2.0);
3891 NoteDrag::motion (GdkEvent *, bool)
3893 /* Total change in x and y since the start of the drag */
3894 frameoffset_t const dx = total_dx ();
3895 int8_t const dy = total_dy ();
3897 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3898 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3899 double const tdy = dy * _note_height - _cumulative_dy;
3902 _region->move_selection (tdx, tdy);
3903 _cumulative_dx += tdx;
3904 _cumulative_dy += tdy;
3907 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3908 (int) floor (_primary->note()->note() + dy));
3910 _editor->show_verbose_canvas_cursor_with (buf);
3915 NoteDrag::finished (GdkEvent* ev, bool moved)
3918 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3920 if (_was_selected) {
3921 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3923 _region->note_deselected (_primary);
3926 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3927 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3929 if (!extend && !add && _region->selection_size() > 1) {
3930 _region->unique_select (_primary);
3931 } else if (extend) {
3932 _region->note_selected (_primary, true, true);
3934 /* it was added during button press */
3939 _region->note_dropped (_primary, total_dx(), - total_dy());
3944 NoteDrag::aborted ()
3949 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3952 , _nothing_to_drag (false)
3954 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3957 _line = _atav->line ();
3961 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3963 Drag::start_grab (event, cursor);
3965 list<ControlPoint*> points;
3967 XMLNode* state = &_line->get_state ();
3969 if (_ranges.empty()) {
3971 uint32_t const N = _line->npoints ();
3972 for (uint32_t i = 0; i < N; ++i) {
3973 points.push_back (_line->nth (i));
3978 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3979 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3981 /* fade into and out of the region that we're dragging;
3982 64 samples length plucked out of thin air.
3984 nframes64_t const h = (j->start + j->end) / 2;
3985 nframes64_t a = j->start + 64;
3989 nframes64_t b = j->end - 64;
3994 the_list->add (j->start, the_list->eval (j->start));
3995 _line->add_always_in_view (j->start);
3996 the_list->add (a, the_list->eval (a));
3997 _line->add_always_in_view (a);
3998 the_list->add (b, the_list->eval (b));
3999 _line->add_always_in_view (b);
4000 the_list->add (j->end, the_list->eval (j->end));
4001 _line->add_always_in_view (j->end);
4004 uint32_t const N = _line->npoints ();
4005 for (uint32_t i = 0; i < N; ++i) {
4007 ControlPoint* p = _line->nth (i);
4009 list<AudioRange>::const_iterator j = _ranges.begin ();
4010 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
4014 if (j != _ranges.end()) {
4015 points.push_back (p);
4020 if (points.empty()) {
4021 _nothing_to_drag = true;
4025 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
4029 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
4031 if (_nothing_to_drag) {
4035 float const f = 1 - (_drags->current_pointer_y() / _line->height());
4037 /* we are ignoring x position for this drag, so we can just pass in anything */
4038 _line->drag_motion (0, f, true, false);
4042 AutomationRangeDrag::finished (GdkEvent* event, bool)
4044 if (_nothing_to_drag) {
4048 motion (event, false);
4050 _line->clear_always_in_view ();
4054 AutomationRangeDrag::aborted ()
4056 _line->clear_always_in_view ();
4060 DraggingView::DraggingView (RegionView* v)
4063 initial_y = v->get_canvas_group()->property_y ();
4064 initial_playlist = v->region()->playlist ();