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_history ();
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_history () 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_history ();
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_history () 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_history ();
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_history ();
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 *, 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 if (_drags->current_pointer_frame() < grab_frame()) {
1606 _region->set_position (_drags->current_pointer_frame(), this);
1609 /* again, don't use a zero-length region (see above) */
1610 framecnt_t const len = abs (_drags->current_pointer_frame() - grab_frame ());
1611 _region->set_length (len < 1 ? 1 : len, this);
1616 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1618 if (movement_occurred) {
1619 _editor->commit_reversible_command ();
1624 RegionCreateDrag::aborted ()
1629 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1637 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1639 Gdk::Cursor* cursor;
1640 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1642 Drag::start_grab (event);
1644 region = &cnote->region_view();
1646 double const region_start = region->get_position_pixels();
1647 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1649 if (grab_x() <= middle_point) {
1650 cursor = _editor->left_side_trim_cursor;
1653 cursor = _editor->right_side_trim_cursor;
1657 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1659 if (event->motion.state & Keyboard::PrimaryModifier) {
1665 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1667 if (ms.size() > 1) {
1668 /* has to be relative, may make no sense otherwise */
1672 /* select this note; if it is already selected, preserve the existing selection,
1673 otherwise make this note the only one selected.
1675 region->note_selected (cnote, cnote->selected ());
1677 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1678 MidiRegionSelection::iterator next;
1681 (*r)->begin_resizing (at_front);
1687 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1689 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1690 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1691 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1696 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1698 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1699 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1700 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1705 NoteResizeDrag::aborted ()
1711 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1717 RegionGainDrag::finished (GdkEvent *, bool)
1723 RegionGainDrag::aborted ()
1728 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1729 : RegionDrag (e, i, p, v)
1730 , _have_transaction (false)
1736 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1739 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1740 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1742 if (tv && tv->is_track()) {
1743 speed = tv->track()->speed();
1746 nframes64_t const region_start = (nframes64_t) (_primary->region()->position() / speed);
1747 nframes64_t const region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1748 nframes64_t const region_length = (nframes64_t) (_primary->region()->length() / speed);
1750 nframes64_t const pf = adjusted_current_frame (event);
1752 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1753 _operation = ContentsTrim;
1754 Drag::start_grab (event, _editor->trimmer_cursor);
1756 /* These will get overridden for a point trim.*/
1757 if (pf < (region_start + region_length/2)) {
1758 /* closer to start */
1759 _operation = StartTrim;
1760 Drag::start_grab (event, _editor->left_side_trim_cursor);
1763 _operation = EndTrim;
1764 Drag::start_grab (event, _editor->right_side_trim_cursor);
1768 switch (_operation) {
1770 _editor->show_verbose_time_cursor (region_start, 10);
1773 _editor->show_verbose_time_cursor (region_end, 10);
1776 _editor->show_verbose_time_cursor (pf, 10);
1782 TrimDrag::motion (GdkEvent* event, bool first_move)
1784 RegionView* rv = _primary;
1786 /* snap modifier works differently here..
1787 its current state has to be passed to the
1788 various trim functions in order to work properly
1792 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1793 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1794 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1796 if (tv && tv->is_track()) {
1797 speed = tv->track()->speed();
1800 nframes64_t const pf = adjusted_current_frame (event);
1806 switch (_operation) {
1808 trim_type = "Region start trim";
1811 trim_type = "Region end trim";
1814 trim_type = "Region content trim";
1818 _editor->begin_reversible_command (trim_type);
1819 _have_transaction = true;
1821 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1822 RegionView* rv = i->view;
1823 rv->fake_set_opaque(false);
1824 rv->enable_display (false);
1825 rv->region()->clear_history ();
1826 rv->region()->suspend_property_changes ();
1828 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1831 arv->temporarily_hide_envelope ();
1834 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1835 insert_result = _editor->motion_frozen_playlists.insert (pl);
1837 if (insert_result.second) {
1843 bool non_overlap_trim = false;
1845 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1846 non_overlap_trim = true;
1849 switch (_operation) {
1851 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1852 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1857 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1858 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1864 bool swap_direction = false;
1866 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1867 swap_direction = true;
1870 nframes64_t frame_delta = 0;
1872 bool left_direction = false;
1873 if (last_pointer_frame() > pf) {
1874 left_direction = true;
1877 if (left_direction) {
1878 frame_delta = (last_pointer_frame() - pf);
1880 frame_delta = (pf - last_pointer_frame());
1883 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1884 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1890 switch (_operation) {
1892 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1895 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1898 _editor->show_verbose_time_cursor (pf, 10);
1905 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1907 if (movement_occurred) {
1908 motion (event, false);
1910 if (!_editor->selection->selected (_primary)) {
1911 _editor->thaw_region_after_trim (*_primary);
1914 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1915 _editor->thaw_region_after_trim (*i->view);
1916 i->view->enable_display (true);
1917 i->view->fake_set_opaque (true);
1918 if (_have_transaction) {
1919 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1923 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1927 _editor->motion_frozen_playlists.clear ();
1929 if (_have_transaction) {
1930 _editor->commit_reversible_command();
1934 /* no mouse movement */
1935 _editor->point_trim (event, adjusted_current_frame (event));
1940 TrimDrag::aborted ()
1942 /* Our motion method is changing model state, so use the Undo system
1943 to cancel. Perhaps not ideal, as this will leave an Undo point
1944 behind which may be slightly odd from the user's point of view.
1949 if (_have_transaction) {
1954 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1958 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1963 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1966 // create a dummy marker for visual representation of moving the copy.
1967 // The actual copying is not done before we reach the finish callback.
1969 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1970 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1971 *new MeterSection (_marker->meter()));
1973 _item = &new_marker->the_item ();
1974 _marker = new_marker;
1978 MetricSection& section (_marker->meter());
1980 if (!section.movable()) {
1986 Drag::start_grab (event, cursor);
1988 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1990 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1994 MeterMarkerDrag::motion (GdkEvent* event, bool)
1996 nframes64_t const pf = adjusted_current_frame (event);
1998 _marker->set_position (pf);
2000 _editor->show_verbose_time_cursor (pf, 10);
2004 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2006 if (!movement_occurred) {
2010 motion (event, false);
2014 TempoMap& map (_editor->session()->tempo_map());
2015 map.bbt_time (last_pointer_frame(), when);
2017 if (_copy == true) {
2018 _editor->begin_reversible_command (_("copy meter mark"));
2019 XMLNode &before = map.get_state();
2020 map.add_meter (_marker->meter(), when);
2021 XMLNode &after = map.get_state();
2022 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2023 _editor->commit_reversible_command ();
2025 // delete the dummy marker we used for visual representation of copying.
2026 // a new visual marker will show up automatically.
2029 _editor->begin_reversible_command (_("move meter mark"));
2030 XMLNode &before = map.get_state();
2031 map.move_meter (_marker->meter(), when);
2032 XMLNode &after = map.get_state();
2033 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2034 _editor->commit_reversible_command ();
2039 MeterMarkerDrag::aborted ()
2041 _marker->set_position (_marker->meter().frame ());
2044 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2048 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2053 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2058 // create a dummy marker for visual representation of moving the copy.
2059 // The actual copying is not done before we reach the finish callback.
2061 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2062 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2063 *new TempoSection (_marker->tempo()));
2065 _item = &new_marker->the_item ();
2066 _marker = new_marker;
2070 MetricSection& section (_marker->tempo());
2072 if (!section.movable()) {
2077 Drag::start_grab (event, cursor);
2079 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2080 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2084 TempoMarkerDrag::motion (GdkEvent* event, bool)
2086 nframes64_t const pf = adjusted_current_frame (event);
2087 _marker->set_position (pf);
2088 _editor->show_verbose_time_cursor (pf, 10);
2092 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2094 if (!movement_occurred) {
2098 motion (event, false);
2102 TempoMap& map (_editor->session()->tempo_map());
2103 map.bbt_time (last_pointer_frame(), when);
2105 if (_copy == true) {
2106 _editor->begin_reversible_command (_("copy tempo mark"));
2107 XMLNode &before = map.get_state();
2108 map.add_tempo (_marker->tempo(), when);
2109 XMLNode &after = map.get_state();
2110 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2111 _editor->commit_reversible_command ();
2113 // delete the dummy marker we used for visual representation of copying.
2114 // a new visual marker will show up automatically.
2117 _editor->begin_reversible_command (_("move tempo mark"));
2118 XMLNode &before = map.get_state();
2119 map.move_tempo (_marker->tempo(), when);
2120 XMLNode &after = map.get_state();
2121 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2122 _editor->commit_reversible_command ();
2127 TempoMarkerDrag::aborted ()
2129 _marker->set_position (_marker->tempo().frame());
2132 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2136 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2141 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2143 Drag::start_grab (event, c);
2147 nframes64_t where = _editor->event_frame (event, 0, 0);
2149 _editor->snap_to_with_modifier (where, event);
2150 _editor->playhead_cursor->set_position (where);
2154 if (_cursor == _editor->playhead_cursor) {
2155 _editor->_dragging_playhead = true;
2157 Session* s = _editor->session ();
2160 if (_was_rolling && _stop) {
2164 if (s->is_auditioning()) {
2165 s->cancel_audition ();
2168 s->request_suspend_timecode_transmission ();
2170 if (s->timecode_transmission_suspended ()) {
2171 nframes64_t const f = _editor->playhead_cursor->current_frame;
2172 s->send_mmc_locate (f);
2173 s->send_full_time_code (f);
2178 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2180 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2184 CursorDrag::motion (GdkEvent* event, bool)
2186 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2188 if (adjusted_frame == last_pointer_frame()) {
2192 _cursor->set_position (adjusted_frame);
2194 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2196 Session* s = _editor->session ();
2197 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2198 nframes64_t const f = _editor->playhead_cursor->current_frame;
2199 s->send_mmc_locate (f);
2200 s->send_full_time_code (f);
2205 _editor->update_canvas_now ();
2207 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2211 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2213 _editor->_dragging_playhead = false;
2215 if (!movement_occurred && _stop) {
2219 motion (event, false);
2221 if (_item == &_editor->playhead_cursor->canvas_item) {
2222 Session* s = _editor->session ();
2224 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2225 _editor->_pending_locate_request = true;
2226 s->request_resume_timecode_transmission ();
2232 CursorDrag::aborted ()
2234 if (_editor->_dragging_playhead) {
2235 _editor->session()->request_resume_timecode_transmission ();
2236 _editor->_dragging_playhead = false;
2239 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2242 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2243 : RegionDrag (e, i, p, v)
2249 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2251 Drag::start_grab (event, cursor);
2253 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2254 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2256 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2257 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2262 FadeInDrag::motion (GdkEvent* event, bool)
2264 nframes64_t fade_length;
2266 nframes64_t const pos = adjusted_current_frame (event);
2268 boost::shared_ptr<Region> region = _primary->region ();
2270 if (pos < (region->position() + 64)) {
2271 fade_length = 64; // this should be a minimum defined somewhere
2272 } else if (pos > region->last_frame()) {
2273 fade_length = region->length();
2275 fade_length = pos - region->position();
2278 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2280 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2286 tmp->reset_fade_in_shape_width (fade_length);
2289 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2293 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2295 if (!movement_occurred) {
2299 nframes64_t fade_length;
2301 nframes64_t const pos = adjusted_current_frame (event);
2303 boost::shared_ptr<Region> region = _primary->region ();
2305 if (pos < (region->position() + 64)) {
2306 fade_length = 64; // this should be a minimum defined somewhere
2307 } else if (pos > region->last_frame()) {
2308 fade_length = region->length();
2310 fade_length = pos - region->position();
2313 _editor->begin_reversible_command (_("change fade in length"));
2315 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2317 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2323 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2324 XMLNode &before = alist->get_state();
2326 tmp->audio_region()->set_fade_in_length (fade_length);
2327 tmp->audio_region()->set_fade_in_active (true);
2329 XMLNode &after = alist->get_state();
2330 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2333 _editor->commit_reversible_command ();
2337 FadeInDrag::aborted ()
2339 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2340 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2346 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2350 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2351 : RegionDrag (e, i, p, v)
2357 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2359 Drag::start_grab (event, cursor);
2361 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2362 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2364 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2365 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2369 FadeOutDrag::motion (GdkEvent* event, bool)
2371 nframes64_t fade_length;
2373 nframes64_t const pos = adjusted_current_frame (event);
2375 boost::shared_ptr<Region> region = _primary->region ();
2377 if (pos > (region->last_frame() - 64)) {
2378 fade_length = 64; // this should really be a minimum fade defined somewhere
2380 else if (pos < region->position()) {
2381 fade_length = region->length();
2384 fade_length = region->last_frame() - pos;
2387 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2389 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2395 tmp->reset_fade_out_shape_width (fade_length);
2398 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2402 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2404 if (!movement_occurred) {
2408 nframes64_t fade_length;
2410 nframes64_t const pos = adjusted_current_frame (event);
2412 boost::shared_ptr<Region> region = _primary->region ();
2414 if (pos > (region->last_frame() - 64)) {
2415 fade_length = 64; // this should really be a minimum fade defined somewhere
2417 else if (pos < region->position()) {
2418 fade_length = region->length();
2421 fade_length = region->last_frame() - pos;
2424 _editor->begin_reversible_command (_("change fade out length"));
2426 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2428 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2434 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2435 XMLNode &before = alist->get_state();
2437 tmp->audio_region()->set_fade_out_length (fade_length);
2438 tmp->audio_region()->set_fade_out_active (true);
2440 XMLNode &after = alist->get_state();
2441 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2444 _editor->commit_reversible_command ();
2448 FadeOutDrag::aborted ()
2450 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2451 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2457 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2461 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2464 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2467 _points.push_back (Gnome::Art::Point (0, 0));
2468 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2470 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2471 _line->property_width_pixels() = 1;
2472 _line->property_points () = _points;
2475 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2478 MarkerDrag::~MarkerDrag ()
2480 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2486 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2488 Drag::start_grab (event, cursor);
2492 Location *location = _editor->find_location_from_marker (_marker, is_start);
2493 _editor->_dragging_edit_point = true;
2495 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2497 update_item (location);
2499 // _drag_line->show();
2500 // _line->raise_to_top();
2503 _editor->show_verbose_time_cursor (location->start(), 10);
2505 _editor->show_verbose_time_cursor (location->end(), 10);
2508 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2511 case Selection::Toggle:
2512 _editor->selection->toggle (_marker);
2514 case Selection::Set:
2515 if (!_editor->selection->selected (_marker)) {
2516 _editor->selection->set (_marker);
2519 case Selection::Extend:
2521 Locations::LocationList ll;
2522 list<Marker*> to_add;
2524 _editor->selection->markers.range (s, e);
2525 s = min (_marker->position(), s);
2526 e = max (_marker->position(), e);
2529 if (e < max_frames) {
2532 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2533 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2534 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2537 to_add.push_back (lm->start);
2540 to_add.push_back (lm->end);
2544 if (!to_add.empty()) {
2545 _editor->selection->add (to_add);
2549 case Selection::Add:
2550 _editor->selection->add (_marker);
2554 /* Set up copies for us to manipulate during the drag */
2556 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2557 Location* l = _editor->find_location_from_marker (*i, is_start);
2558 _copied_locations.push_back (new Location (*l));
2563 MarkerDrag::motion (GdkEvent* event, bool)
2565 nframes64_t f_delta = 0;
2567 bool move_both = false;
2569 Location *real_location;
2570 Location *copy_location = 0;
2572 nframes64_t const newframe = adjusted_current_frame (event);
2574 nframes64_t next = newframe;
2576 if (newframe == last_pointer_frame()) {
2580 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2584 MarkerSelection::iterator i;
2585 list<Location*>::iterator x;
2587 /* find the marker we're dragging, and compute the delta */
2589 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2590 x != _copied_locations.end() && i != _editor->selection->markers.end();
2596 if (marker == _marker) {
2598 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2603 if (real_location->is_mark()) {
2604 f_delta = newframe - copy_location->start();
2608 switch (marker->type()) {
2610 case Marker::LoopStart:
2611 case Marker::PunchIn:
2612 f_delta = newframe - copy_location->start();
2616 case Marker::LoopEnd:
2617 case Marker::PunchOut:
2618 f_delta = newframe - copy_location->end();
2621 /* what kind of marker is this ? */
2629 if (i == _editor->selection->markers.end()) {
2630 /* hmm, impossible - we didn't find the dragged marker */
2634 /* now move them all */
2636 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2637 x != _copied_locations.end() && i != _editor->selection->markers.end();
2643 /* call this to find out if its the start or end */
2645 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2649 if (real_location->locked()) {
2653 if (copy_location->is_mark()) {
2657 copy_location->set_start (copy_location->start() + f_delta);
2661 nframes64_t new_start = copy_location->start() + f_delta;
2662 nframes64_t new_end = copy_location->end() + f_delta;
2664 if (is_start) { // start-of-range marker
2667 copy_location->set_start (new_start);
2668 copy_location->set_end (new_end);
2669 } else if (new_start < copy_location->end()) {
2670 copy_location->set_start (new_start);
2672 _editor->snap_to (next, 1, true);
2673 copy_location->set_end (next);
2674 copy_location->set_start (newframe);
2677 } else { // end marker
2680 copy_location->set_end (new_end);
2681 copy_location->set_start (new_start);
2682 } else if (new_end > copy_location->start()) {
2683 copy_location->set_end (new_end);
2684 } else if (newframe > 0) {
2685 _editor->snap_to (next, -1, true);
2686 copy_location->set_start (next);
2687 copy_location->set_end (newframe);
2692 update_item (copy_location);
2694 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2697 lm->set_position (copy_location->start(), copy_location->end());
2701 assert (!_copied_locations.empty());
2703 _editor->show_verbose_time_cursor (newframe, 10);
2706 _editor->update_canvas_now ();
2711 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2713 if (!movement_occurred) {
2715 /* just a click, do nothing but finish
2716 off the selection process
2719 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2722 case Selection::Set:
2723 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2724 _editor->selection->set (_marker);
2728 case Selection::Toggle:
2729 case Selection::Extend:
2730 case Selection::Add:
2737 _editor->_dragging_edit_point = false;
2739 _editor->begin_reversible_command ( _("move marker") );
2740 XMLNode &before = _editor->session()->locations()->get_state();
2742 MarkerSelection::iterator i;
2743 list<Location*>::iterator x;
2746 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2747 x != _copied_locations.end() && i != _editor->selection->markers.end();
2750 Location * location = _editor->find_location_from_marker (*i, is_start);
2754 if (location->locked()) {
2758 if (location->is_mark()) {
2759 location->set_start ((*x)->start());
2761 location->set ((*x)->start(), (*x)->end());
2766 XMLNode &after = _editor->session()->locations()->get_state();
2767 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2768 _editor->commit_reversible_command ();
2774 MarkerDrag::aborted ()
2780 MarkerDrag::update_item (Location* location)
2782 double const x1 = _editor->frame_to_pixel (location->start());
2784 _points.front().set_x(x1);
2785 _points.back().set_x(x1);
2786 _line->property_points() = _points;
2789 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2791 _cumulative_x_drag (0),
2792 _cumulative_y_drag (0)
2794 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2800 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2802 Drag::start_grab (event, _editor->fader_cursor);
2804 // start the grab at the center of the control point so
2805 // the point doesn't 'jump' to the mouse after the first drag
2806 _fixed_grab_x = _point->get_x();
2807 _fixed_grab_y = _point->get_y();
2809 float const fraction = 1 - (_point->get_y() / _point->line().height());
2811 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2813 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2814 event->button.x + 10, event->button.y + 10);
2816 _editor->show_verbose_canvas_cursor ();
2820 ControlPointDrag::motion (GdkEvent* event, bool)
2822 double dx = _drags->current_pointer_x() - last_pointer_x();
2823 double dy = _drags->current_pointer_y() - last_pointer_y();
2825 if (event->button.state & Keyboard::SecondaryModifier) {
2830 /* coordinate in pixels relative to the start of the region (for region-based automation)
2831 or track (for track-based automation) */
2832 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2833 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2835 // calculate zero crossing point. back off by .01 to stay on the
2836 // positive side of zero
2837 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2839 // make sure we hit zero when passing through
2840 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2844 if (_x_constrained) {
2847 if (_y_constrained) {
2851 _cumulative_x_drag = cx - _fixed_grab_x;
2852 _cumulative_y_drag = cy - _fixed_grab_y;
2856 cy = min ((double) _point->line().height(), cy);
2858 framepos_t cx_frames = _editor->unit_to_frame (cx);
2860 if (!_x_constrained) {
2861 _editor->snap_to_with_modifier (cx_frames, event);
2864 cx_frames = min (cx_frames, _point->line().maximum_time());
2866 float const fraction = 1.0 - (cy / _point->line().height());
2868 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2870 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2872 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2876 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2878 if (!movement_occurred) {
2882 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2883 _editor->reset_point_selection ();
2887 motion (event, false);
2889 _point->line().end_drag ();
2893 ControlPointDrag::aborted ()
2895 _point->line().reset ();
2899 ControlPointDrag::active (Editing::MouseMode m)
2901 if (m == Editing::MouseGain) {
2902 /* always active in mouse gain */
2906 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2907 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2910 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2913 _cumulative_y_drag (0)
2918 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2920 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2923 _item = &_line->grab_item ();
2925 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2926 origin, and ditto for y.
2929 double cx = event->button.x;
2930 double cy = event->button.y;
2932 _line->parent_group().w2i (cx, cy);
2934 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2939 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2940 /* no adjacent points */
2944 Drag::start_grab (event, _editor->fader_cursor);
2946 /* store grab start in parent frame */
2951 double fraction = 1.0 - (cy / _line->height());
2953 _line->start_drag_line (before, after, fraction);
2955 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2956 event->button.x + 10, event->button.y + 10);
2958 _editor->show_verbose_canvas_cursor ();
2962 LineDrag::motion (GdkEvent* event, bool)
2964 double dy = _drags->current_pointer_y() - last_pointer_y();
2966 if (event->button.state & Keyboard::SecondaryModifier) {
2970 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2972 _cumulative_y_drag = cy - _fixed_grab_y;
2975 cy = min ((double) _line->height(), cy);
2977 double const fraction = 1.0 - (cy / _line->height());
2981 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2987 /* we are ignoring x position for this drag, so we can just pass in anything */
2988 _line->drag_motion (0, fraction, true, push);
2990 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2994 LineDrag::finished (GdkEvent* event, bool)
2996 motion (event, false);
3001 LineDrag::aborted ()
3006 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3009 _cumulative_x_drag (0)
3014 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3017 Drag::start_grab (event);
3019 _line = reinterpret_cast<SimpleLine*> (_item);
3022 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3024 double cx = event->button.x;
3025 double cy = event->button.y;
3027 _item->property_parent().get_value()->w2i(cx, cy);
3029 /* store grab start in parent frame */
3030 _region_view_grab_x = cx;
3032 _before = _line->property_x1();
3034 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3036 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3040 FeatureLineDrag::motion (GdkEvent* event, bool)
3042 double dx = _drags->current_pointer_x() - last_pointer_x();
3044 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3046 _cumulative_x_drag += dx;
3048 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3057 _line->property_x1() = cx;
3058 _line->property_x2() = cx;
3060 _before = _line->property_x1();
3064 FeatureLineDrag::finished (GdkEvent* event, bool)
3066 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3067 _arv->update_transient(_before, _line->property_x1());
3071 FeatureLineDrag::aborted ()
3077 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3079 Drag::start_grab (event);
3080 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3084 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3091 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3093 nframes64_t grab = grab_frame ();
3094 if (Config->get_rubberbanding_snaps_to_grid ()) {
3095 _editor->snap_to_with_modifier (grab, event);
3098 /* base start and end on initial click position */
3108 if (_drags->current_pointer_y() < grab_y()) {
3109 y1 = _drags->current_pointer_y();
3112 y2 = _drags->current_pointer_y();
3117 if (start != end || y1 != y2) {
3119 double x1 = _editor->frame_to_pixel (start);
3120 double x2 = _editor->frame_to_pixel (end);
3122 _editor->rubberband_rect->property_x1() = x1;
3123 _editor->rubberband_rect->property_y1() = y1;
3124 _editor->rubberband_rect->property_x2() = x2;
3125 _editor->rubberband_rect->property_y2() = y2;
3127 _editor->rubberband_rect->show();
3128 _editor->rubberband_rect->raise_to_top();
3130 _editor->show_verbose_time_cursor (pf, 10);
3135 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3137 if (movement_occurred) {
3139 motion (event, false);
3142 if (_drags->current_pointer_y() < grab_y()) {
3143 y1 = _drags->current_pointer_y();
3146 y2 = _drags->current_pointer_y();
3151 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3154 _editor->begin_reversible_command (_("rubberband selection"));
3156 if (grab_frame() < last_pointer_frame()) {
3157 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3159 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3163 _editor->commit_reversible_command ();
3167 if (!getenv("ARDOUR_SAE")) {
3168 _editor->selection->clear_tracks();
3170 _editor->selection->clear_regions();
3171 _editor->selection->clear_points ();
3172 _editor->selection->clear_lines ();
3175 _editor->rubberband_rect->hide();
3179 RubberbandSelectDrag::aborted ()
3181 _editor->rubberband_rect->hide ();
3185 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3187 Drag::start_grab (event);
3189 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3193 TimeFXDrag::motion (GdkEvent* event, bool)
3195 RegionView* rv = _primary;
3197 nframes64_t const pf = adjusted_current_frame (event);
3199 if (pf > rv->region()->position()) {
3200 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3203 _editor->show_verbose_time_cursor (pf, 10);
3207 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3209 _primary->get_time_axis_view().hide_timestretch ();
3211 if (!movement_occurred) {
3215 if (last_pointer_frame() < _primary->region()->position()) {
3216 /* backwards drag of the left edge - not usable */
3220 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3222 float percentage = (double) newlen / (double) _primary->region()->length();
3224 #ifndef USE_RUBBERBAND
3225 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3226 if (_primary->region()->data_type() == DataType::AUDIO) {
3227 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3231 _editor->begin_reversible_command (_("timestretch"));
3233 // XXX how do timeFX on multiple regions ?
3238 if (_editor->time_stretch (rs, percentage) == -1) {
3239 error << _("An error occurred while executing time stretch operation") << endmsg;
3244 TimeFXDrag::aborted ()
3246 _primary->get_time_axis_view().hide_timestretch ();
3251 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3253 Drag::start_grab (event);
3257 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3259 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3263 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3265 if (movement_occurred && _editor->session()) {
3266 /* make sure we stop */
3267 _editor->session()->request_transport_speed (0.0);
3272 ScrubDrag::aborted ()
3277 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3281 , _original_pointer_time_axis (-1)
3282 , _last_pointer_time_axis (-1)
3288 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3290 nframes64_t start = 0;
3291 nframes64_t end = 0;
3293 if (_editor->session() == 0) {
3297 Gdk::Cursor* cursor = 0;
3299 switch (_operation) {
3300 case CreateSelection:
3301 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3306 cursor = _editor->selector_cursor;
3307 Drag::start_grab (event, cursor);
3310 case SelectionStartTrim:
3311 if (_editor->clicked_axisview) {
3312 _editor->clicked_axisview->order_selection_trims (_item, true);
3314 Drag::start_grab (event, _editor->left_side_trim_cursor);
3315 start = _editor->selection->time[_editor->clicked_selection].start;
3316 _pointer_frame_offset = grab_frame() - start;
3319 case SelectionEndTrim:
3320 if (_editor->clicked_axisview) {
3321 _editor->clicked_axisview->order_selection_trims (_item, false);
3323 Drag::start_grab (event, _editor->right_side_trim_cursor);
3324 end = _editor->selection->time[_editor->clicked_selection].end;
3325 _pointer_frame_offset = grab_frame() - end;
3329 start = _editor->selection->time[_editor->clicked_selection].start;
3330 Drag::start_grab (event, cursor);
3331 _pointer_frame_offset = grab_frame() - start;
3335 if (_operation == SelectionMove) {
3336 _editor->show_verbose_time_cursor (start, 10);
3338 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3341 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3345 SelectionDrag::motion (GdkEvent* event, bool first_move)
3347 nframes64_t start = 0;
3348 nframes64_t end = 0;
3351 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3352 if (pending_time_axis.first == 0) {
3356 nframes64_t const pending_position = adjusted_current_frame (event);
3358 /* only alter selection if things have changed */
3360 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3364 switch (_operation) {
3365 case CreateSelection:
3367 nframes64_t grab = grab_frame ();
3370 _editor->snap_to (grab);
3373 if (pending_position < grab_frame()) {
3374 start = pending_position;
3377 end = pending_position;
3381 /* first drag: Either add to the selection
3382 or create a new selection
3388 /* adding to the selection */
3389 _editor->selection->add (_editor->clicked_axisview);
3390 _editor->clicked_selection = _editor->selection->add (start, end);
3395 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3396 _editor->selection->set (_editor->clicked_axisview);
3399 _editor->clicked_selection = _editor->selection->set (start, end);
3403 /* select the track that we're in */
3404 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3405 _editor->selection->add (pending_time_axis.first);
3406 _added_time_axes.push_back (pending_time_axis.first);
3409 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3410 tracks that we selected in the first place.
3413 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3414 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3416 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3417 while (i != _added_time_axes.end()) {
3419 list<TimeAxisView*>::iterator tmp = i;
3422 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3423 _editor->selection->remove (*i);
3424 _added_time_axes.remove (*i);
3433 case SelectionStartTrim:
3435 start = _editor->selection->time[_editor->clicked_selection].start;
3436 end = _editor->selection->time[_editor->clicked_selection].end;
3438 if (pending_position > end) {
3441 start = pending_position;
3445 case SelectionEndTrim:
3447 start = _editor->selection->time[_editor->clicked_selection].start;
3448 end = _editor->selection->time[_editor->clicked_selection].end;
3450 if (pending_position < start) {
3453 end = pending_position;
3460 start = _editor->selection->time[_editor->clicked_selection].start;
3461 end = _editor->selection->time[_editor->clicked_selection].end;
3463 length = end - start;
3465 start = pending_position;
3466 _editor->snap_to (start);
3468 end = start + length;
3473 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3474 _editor->start_canvas_autoscroll (1, 0);
3478 _editor->selection->replace (_editor->clicked_selection, start, end);
3481 if (_operation == SelectionMove) {
3482 _editor->show_verbose_time_cursor(start, 10);
3484 _editor->show_verbose_time_cursor(pending_position, 10);
3489 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3491 Session* s = _editor->session();
3493 if (movement_occurred) {
3494 motion (event, false);
3495 /* XXX this is not object-oriented programming at all. ick */
3496 if (_editor->selection->time.consolidate()) {
3497 _editor->selection->TimeChanged ();
3500 /* XXX what if its a music time selection? */
3501 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3502 s->request_play_range (&_editor->selection->time, true);
3507 /* just a click, no pointer movement.*/
3509 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3510 _editor->selection->clear_time();
3513 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3514 _editor->selection->set (_editor->clicked_axisview);
3517 if (s && s->get_play_range () && s->transport_rolling()) {
3518 s->request_stop (false, false);
3523 _editor->stop_canvas_autoscroll ();
3527 SelectionDrag::aborted ()
3532 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3537 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3538 physical_screen_height (_editor->get_window()));
3539 _drag_rect->hide ();
3541 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3542 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3546 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3548 if (_editor->session() == 0) {
3552 Gdk::Cursor* cursor = 0;
3554 if (!_editor->temp_location) {
3555 _editor->temp_location = new Location (*_editor->session());
3558 switch (_operation) {
3559 case CreateRangeMarker:
3560 case CreateTransportMarker:
3561 case CreateCDMarker:
3563 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3568 cursor = _editor->selector_cursor;
3572 Drag::start_grab (event, cursor);
3574 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3578 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3580 nframes64_t start = 0;
3581 nframes64_t end = 0;
3582 ArdourCanvas::SimpleRect *crect;
3584 switch (_operation) {
3585 case CreateRangeMarker:
3586 crect = _editor->range_bar_drag_rect;
3588 case CreateTransportMarker:
3589 crect = _editor->transport_bar_drag_rect;
3591 case CreateCDMarker:
3592 crect = _editor->cd_marker_bar_drag_rect;
3595 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3600 nframes64_t const pf = adjusted_current_frame (event);
3602 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3603 nframes64_t grab = grab_frame ();
3604 _editor->snap_to (grab);
3606 if (pf < grab_frame()) {
3614 /* first drag: Either add to the selection
3615 or create a new selection.
3620 _editor->temp_location->set (start, end);
3624 update_item (_editor->temp_location);
3626 //_drag_rect->raise_to_top();
3631 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3632 _editor->start_canvas_autoscroll (1, 0);
3636 _editor->temp_location->set (start, end);
3638 double x1 = _editor->frame_to_pixel (start);
3639 double x2 = _editor->frame_to_pixel (end);
3640 crect->property_x1() = x1;
3641 crect->property_x2() = x2;
3643 update_item (_editor->temp_location);
3646 _editor->show_verbose_time_cursor (pf, 10);
3651 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3653 Location * newloc = 0;
3657 if (movement_occurred) {
3658 motion (event, false);
3661 switch (_operation) {
3662 case CreateRangeMarker:
3663 case CreateCDMarker:
3665 _editor->begin_reversible_command (_("new range marker"));
3666 XMLNode &before = _editor->session()->locations()->get_state();
3667 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3668 if (_operation == CreateCDMarker) {
3669 flags = Location::IsRangeMarker | Location::IsCDMarker;
3670 _editor->cd_marker_bar_drag_rect->hide();
3673 flags = Location::IsRangeMarker;
3674 _editor->range_bar_drag_rect->hide();
3676 newloc = new Location (
3677 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3680 _editor->session()->locations()->add (newloc, true);
3681 XMLNode &after = _editor->session()->locations()->get_state();
3682 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3683 _editor->commit_reversible_command ();
3687 case CreateTransportMarker:
3688 // popup menu to pick loop or punch
3689 _editor->new_transport_marker_context_menu (&event->button, _item);
3693 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3695 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3700 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3702 if (end == max_frames) {
3703 end = _editor->session()->current_end_frame ();
3706 if (start == max_frames) {
3707 start = _editor->session()->current_start_frame ();
3710 switch (_editor->mouse_mode) {
3712 /* find the two markers on either side and then make the selection from it */
3713 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3717 /* find the two markers on either side of the click and make the range out of it */
3718 _editor->selection->set (start, end);
3727 _editor->stop_canvas_autoscroll ();
3731 RangeMarkerBarDrag::aborted ()
3737 RangeMarkerBarDrag::update_item (Location* location)
3739 double const x1 = _editor->frame_to_pixel (location->start());
3740 double const x2 = _editor->frame_to_pixel (location->end());
3742 _drag_rect->property_x1() = x1;
3743 _drag_rect->property_x2() = x2;
3747 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3749 Drag::start_grab (event, _editor->zoom_cursor);
3750 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3754 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3759 nframes64_t const pf = adjusted_current_frame (event);
3761 nframes64_t grab = grab_frame ();
3762 _editor->snap_to_with_modifier (grab, event);
3764 /* base start and end on initial click position */
3776 _editor->zoom_rect->show();
3777 _editor->zoom_rect->raise_to_top();
3780 _editor->reposition_zoom_rect(start, end);
3782 _editor->show_verbose_time_cursor (pf, 10);
3787 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3789 if (movement_occurred) {
3790 motion (event, false);
3792 if (grab_frame() < last_pointer_frame()) {
3793 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3795 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3798 _editor->temporal_zoom_to_frame (false, grab_frame());
3800 temporal_zoom_step (false);
3801 center_screen (grab_frame());
3805 _editor->zoom_rect->hide();
3809 MouseZoomDrag::aborted ()
3811 _editor->zoom_rect->hide ();
3814 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3816 , _cumulative_dx (0)
3817 , _cumulative_dy (0)
3819 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3820 _region = &_primary->region_view ();
3821 _note_height = _region->midi_stream_view()->note_height ();
3825 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3827 Drag::start_grab (event);
3829 if (!(_was_selected = _primary->selected())) {
3831 /* tertiary-click means extend selection - we'll do that on button release,
3832 so don't add it here, because otherwise we make it hard to figure
3833 out the "extend-to" range.
3836 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3839 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3842 _region->note_selected (_primary, true);
3844 _region->unique_select (_primary);
3850 /** @return Current total drag x change in frames */
3852 NoteDrag::total_dx () const
3855 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3857 /* primary note time */
3858 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3860 /* new time of the primary note relative to the region position */
3861 frameoffset_t const st = n + dx;
3863 /* snap and return corresponding delta */
3864 return _region->snap_frame_to_frame (st) - n;
3867 /** @return Current total drag y change in notes */
3869 NoteDrag::total_dy () const
3871 /* this is `backwards' to make increasing note number go in the right direction */
3872 double const dy = _drags->current_pointer_y() - grab_y();
3877 if (abs (dy) >= _note_height) {
3879 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3881 ndy = (int8_t) floor (dy / _note_height / 2.0);
3890 NoteDrag::motion (GdkEvent *, bool)
3892 /* Total change in x and y since the start of the drag */
3893 frameoffset_t const dx = total_dx ();
3894 int8_t const dy = total_dy ();
3896 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3897 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3898 double const tdy = dy * _note_height - _cumulative_dy;
3901 _region->move_selection (tdx, tdy);
3902 _cumulative_dx += tdx;
3903 _cumulative_dy += tdy;
3906 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3907 (int) floor (_primary->note()->note() + dy));
3909 _editor->show_verbose_canvas_cursor_with (buf);
3914 NoteDrag::finished (GdkEvent* ev, bool moved)
3917 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3919 if (_was_selected) {
3920 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3922 _region->note_deselected (_primary);
3925 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3926 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3928 if (!extend && !add && _region->selection_size() > 1) {
3929 _region->unique_select (_primary);
3930 } else if (extend) {
3931 _region->note_selected (_primary, true, true);
3933 /* it was added during button press */
3938 _region->note_dropped (_primary, total_dx(), - total_dy());
3943 NoteDrag::aborted ()
3948 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3951 , _nothing_to_drag (false)
3953 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3956 _line = _atav->line ();
3960 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3962 Drag::start_grab (event, cursor);
3964 list<ControlPoint*> points;
3966 XMLNode* state = &_line->get_state ();
3968 if (_ranges.empty()) {
3970 uint32_t const N = _line->npoints ();
3971 for (uint32_t i = 0; i < N; ++i) {
3972 points.push_back (_line->nth (i));
3977 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3978 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3980 /* fade into and out of the region that we're dragging;
3981 64 samples length plucked out of thin air.
3983 nframes64_t const h = (j->start + j->end) / 2;
3984 nframes64_t a = j->start + 64;
3988 nframes64_t b = j->end - 64;
3993 the_list->add (j->start, the_list->eval (j->start));
3994 _line->add_always_in_view (j->start);
3995 the_list->add (a, the_list->eval (a));
3996 _line->add_always_in_view (a);
3997 the_list->add (b, the_list->eval (b));
3998 _line->add_always_in_view (b);
3999 the_list->add (j->end, the_list->eval (j->end));
4000 _line->add_always_in_view (j->end);
4003 uint32_t const N = _line->npoints ();
4004 for (uint32_t i = 0; i < N; ++i) {
4006 ControlPoint* p = _line->nth (i);
4008 list<AudioRange>::const_iterator j = _ranges.begin ();
4009 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
4013 if (j != _ranges.end()) {
4014 points.push_back (p);
4019 if (points.empty()) {
4020 _nothing_to_drag = true;
4024 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
4028 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
4030 if (_nothing_to_drag) {
4034 float const f = 1 - (_drags->current_pointer_y() / _line->height());
4036 /* we are ignoring x position for this drag, so we can just pass in anything */
4037 _line->drag_motion (0, f, true, false);
4041 AutomationRangeDrag::finished (GdkEvent* event, bool)
4043 if (_nothing_to_drag) {
4047 motion (event, false);
4049 _line->clear_always_in_view ();
4053 AutomationRangeDrag::aborted ()
4055 _line->clear_always_in_view ();
4059 DraggingView::DraggingView (RegionView* v)
4062 initial_y = v->get_canvas_group()->property_y ();
4063 initial_playlist = v->region()->playlist ();